/**
 * WB Listora — UI components (compiled output)
 *
 * GENERATED by bin/build-css.mjs — do NOT edit directly.
 * Edit the source files in src/ and run `npm run build:css`.
 *
 * @package WBListora
 */

/* === src/components/form-field.css === */
/**
 * WB Listora — .listora-form-field primitive
 *
 * Canonical form-field primitive. Every form input in the plugin (submission
 * wizard, review form, post-need form, profile form, claim form, lead form)
 * uses this BEM root. Replaces the per-block inventions:
 *   .listora-submission__field (submission)
 *   .listora-submission-field   (some steps inconsistently named)
 *   .listora-form__input        (details step)
 *
 * Composition:
 *   <div class="listora-form-field" data-state="default">
 *     <label class="listora-form-field__label" for="..." data-required>...</label>
 *     <input class="listora-form-field__input" id="..." type="..." />
 *     <p class="listora-form-field__hint">Optional helper text</p>
 *     <p class="listora-form-field__error">Validation error message</p>
 *   </div>
 *
 * States:
 *   data-state="invalid"  — applies danger ring + error styling
 *   data-state="disabled" — applies disabled styling
 *
 * @since 1.0.5
 */

.listora-form-field {
	display: flex;
	flex-direction: column;
	gap: var(--listora-space-2);
}

.listora-form-field__label {
	font-size: var(--listora-text-size-sm);
	font-weight: var(--listora-weight-medium);
	color: var(--listora-fg-strong);
	line-height: var(--listora-line-tight);
}

.listora-form-field__label[data-required]::after {
	content: " *";
	color: var(--listora-danger);
}

.listora-form-field__input,
.listora-form-field__select,
.listora-form-field__textarea {
	padding: var(--listora-form-control-padding) var(--listora-space-3);
	border: 1px solid var(--listora-border-default);
	border-radius: var(--listora-input-radius);
	font-size: var(--listora-text-size-base);
	font-family: inherit;
	color: var(--listora-fg-default);
	background: var(--listora-bg-elevated);
	transition: border-color var(--listora-transition-fast), box-shadow var(--listora-transition-fast);
	min-height: var(--listora-tap-target);
	width: 100%;
	box-sizing: border-box;
}

.listora-form-field__textarea {
	resize: vertical;
	min-height: calc(var(--listora-tap-target) * 2);
}

.listora-form-field__input::placeholder,
.listora-form-field__textarea::placeholder {
	color: var(--listora-fg-faint);
}

.listora-form-field__input:focus,
.listora-form-field__select:focus,
.listora-form-field__textarea:focus {
	border-color: var(--listora-primary);
	outline: 2px solid transparent;
	outline-offset: 2px;
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--listora-primary) 25%, transparent);
}

/* Invalid state */
.listora-form-field[data-state="invalid"] .listora-form-field__input,
.listora-form-field[data-state="invalid"] .listora-form-field__select,
.listora-form-field[data-state="invalid"] .listora-form-field__textarea {
	border-color: var(--listora-danger);
}

.listora-form-field[data-state="invalid"] .listora-form-field__input:focus,
.listora-form-field[data-state="invalid"] .listora-form-field__select:focus,
.listora-form-field[data-state="invalid"] .listora-form-field__textarea:focus {
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--listora-danger) 25%, transparent);
}

/* Disabled state */
.listora-form-field[data-state="disabled"] {
	opacity: 0.6;
	pointer-events: none;
}

/* Error + hint text */
.listora-form-field__error {
	font-size: var(--listora-text-size-sm);
	color: var(--listora-fg-danger);
	margin: 0;
	line-height: var(--listora-line-snug);
}

.listora-form-field__hint {
	font-size: var(--listora-text-size-sm);
	color: var(--listora-fg-muted);
	margin: 0;
	line-height: var(--listora-line-snug);
}

/* Inline variant — label + input on same row (used for compact filter UIs) */
.listora-form-field--inline {
	flex-direction: row;
	align-items: center;
	gap: var(--listora-space-3);
}

.listora-form-field--inline .listora-form-field__label {
	flex-shrink: 0;
}

/* Checkbox / radio group */
.listora-form-field__checkbox-group,
.listora-form-field__radio-group {
	display: flex;
	flex-direction: column;
	gap: var(--listora-space-2);
}

.listora-form-field__checkbox-group--inline,
.listora-form-field__radio-group--inline {
	flex-direction: row;
	flex-wrap: wrap;
	gap: var(--listora-space-4);
}

.listora-form-field__checkbox,
.listora-form-field__radio {
	display: inline-flex;
	align-items: center;
	gap: var(--listora-space-2);
	cursor: pointer;
}

/* === src/components/button.css === */
/**
 * WB Listora — .listora-btn primitive
 *
 * Canonical button primitive. Replaces the per-block button inventions
 * (.listora-detail__claim-cta, .listora-reviews__helpful-btn,
 * .listora-detail__helpful-btn, .listora-dashboard__action-btn, etc.).
 *
 * Composition:
 *   <button class="listora-btn">Default</button>
 *   <button class="listora-btn listora-btn--primary">Primary action</button>
 *   <button class="listora-btn listora-btn--danger listora-btn--sm">Delete</button>
 *   <a href="..." class="listora-btn listora-btn--ghost">Link as button</a>
 *
 * Variants:
 *   --primary    (filled, brand color — the main action on a surface)
 *   --secondary  (outlined, brand color — secondary action)
 *   --ghost      (no border, no background — tertiary action / links)
 *   --danger     (filled, danger color — destructive action)
 *   --icon       (square, icon-only)
 *
 * Sizes:
 *   --sm  (smaller for compact UIs)
 *   --lg  (larger for hero CTAs)
 *
 * @since 1.0.5
 */

.listora-btn,
a.listora-btn,
a.listora-btn:visited,
a.listora-btn:hover,
a.listora-btn:focus {
	/* Base reset.
	 *
	 * Listora blocks render OUTSIDE `.entry-content`, so themes' aggressive
	 * `.entry-content a:not(...)` resets never reach our buttons. The only
	 * theme rules that do are generic element resets (`button`, `*`) at
	 * specificity 0,0,1 — which `.listora-btn` (0,1,0) and the doubled-class
	 * variants below (0,2,0) outrank cleanly. No !important, no
	 * `wp-element-button` coupling, no per-theme patches. For our own
	 * aggressive themes (BuddyX / BuddyX Pro / Reign) any remaining
	 * reconciliation lives in their dedicated theme bridges. */
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--listora-space-2);

	padding: var(--listora-space-3) var(--listora-space-4);
	min-height: var(--listora-tap-target);

	font-family: inherit;
	font-size: var(--listora-text-size-base);
	font-weight: var(--listora-weight-medium);
	line-height: var(--listora-line-tight);
	text-decoration: none;
	white-space: nowrap;

	background: transparent;
	color: var(--listora-fg-default);
	border: 1px solid transparent;
	border-radius: var(--listora-button-radius);

	cursor: pointer;
	transition: background-color var(--listora-transition-fast),
	            border-color var(--listora-transition-fast),
	            color var(--listora-transition-fast),
	            box-shadow var(--listora-transition-fast);
	user-select: none;
}

.listora-btn:focus-visible {
	outline: 2px solid transparent;
	outline-offset: 2px;
	box-shadow: var(--listora-focus-ring);
}

.listora-btn:disabled,
.listora-btn[aria-disabled="true"] {
	opacity: 0.55;
	cursor: not-allowed;
	pointer-events: none;
}

/* === Variants ===
 *
 * Each variant chains the base class `.listora-btn` with the variant
 * modifier so specificity lifts from 0,1,0 to 0,2,0. That beats any
 * theme's `.entry-content a` (0,1,1) without needing per-theme bridge
 * overrides. The hover/focus pseudo-states are listed explicitly so
 * the theme can't reapply its anchor color on interaction. */

.listora-btn.listora-btn--primary,
a.listora-btn.listora-btn--primary,
a.listora-btn.listora-btn--primary:visited {
	background: var(--listora-primary);
	color: var(--listora-primary-fg);
}

.listora-btn.listora-btn--primary:hover,
.listora-btn.listora-btn--primary:focus,
.listora-btn.listora-btn--primary:focus-visible {
	background: var(--listora-primary-hover);
	color: var(--listora-primary-fg);
}

.listora-btn.listora-btn--secondary,
a.listora-btn.listora-btn--secondary,
a.listora-btn.listora-btn--secondary:visited {
	background: transparent;
	color: var(--listora-primary);
	border-color: var(--listora-primary);
}

.listora-btn.listora-btn--secondary:hover,
.listora-btn.listora-btn--secondary:focus,
.listora-btn.listora-btn--secondary:focus-visible {
	background: color-mix(in srgb, var(--listora-primary) 8%, transparent);
	color: var(--listora-primary);
}

.listora-btn.listora-btn--ghost,
a.listora-btn.listora-btn--ghost,
a.listora-btn.listora-btn--ghost:visited {
	background: transparent;
	color: var(--listora-fg-default);
	border-color: transparent;
}

.listora-btn.listora-btn--ghost:hover,
.listora-btn.listora-btn--ghost:focus,
.listora-btn.listora-btn--ghost:focus-visible {
	background: var(--listora-bg-muted);
	color: var(--listora-fg-default);
}

.listora-btn.listora-btn--danger,
a.listora-btn.listora-btn--danger,
a.listora-btn.listora-btn--danger:visited {
	background: var(--listora-danger);
	color: var(--listora-fg-inverse);
}

.listora-btn.listora-btn--danger:hover,
.listora-btn.listora-btn--danger:focus,
.listora-btn.listora-btn--danger:focus-visible {
	background: var(--listora-danger-fg);
	color: var(--listora-fg-inverse);
}

/* === Sizes === */

.listora-btn--sm {
	padding: var(--listora-space-2) var(--listora-space-3);
	min-height: 32px;
	font-size: var(--listora-text-size-sm);
}

.listora-btn--lg {
	padding: var(--listora-space-4) var(--listora-space-6);
	min-height: 56px;
	font-size: var(--listora-text-size-lg);
}

/* === Icon-only === */

.listora-btn--icon {
	padding: var(--listora-space-2);
	aspect-ratio: 1;
	min-width: var(--listora-tap-target);
}

.listora-btn--icon.listora-btn--sm {
	min-width: 32px;
}

.listora-btn--icon.listora-btn--lg {
	min-width: 56px;
}

/* Icons inside buttons inherit color + size */
.listora-btn svg,
.listora-btn .listora-icon {
	width: 1em;
	height: 1em;
	flex-shrink: 0;
}

/* === Full-width modifier === */

.listora-btn--block {
	display: flex;
	width: 100%;
}

/* === Loading state === */

.listora-btn[aria-busy="true"] {
	cursor: progress;
	pointer-events: none;
	position: relative;
}

.listora-btn[aria-busy="true"]::after {
	content: "";
	position: absolute;
	inset: 0;
	background: inherit;
	opacity: 0.4;
}

/* ============================================================
 * Button vocabulary — doubled-class specificity (no !important).
 * `.listora-btn.listora-btn` (0,2,0) re-asserts the visual props themes'
 * generic element resets touch, holding in any context: block wrappers,
 * the wp_footer Quick View modal, and Leaflet popups. Context-independent
 * because it relies on the element's own classes, not an ancestor scope.
 * ============================================================ */
.listora-btn.listora-btn {
	background: transparent;
	color: var(--listora-fg-default);
	border: 1px solid transparent;
	border-radius: var(--listora-button-radius);
	padding: var(--listora-space-3) var(--listora-space-4);
	min-height: var(--listora-tap-target);
	font-family: inherit;
	font-weight: var(--listora-weight-medium);
	text-transform: none;
	text-shadow: none;
	box-shadow: none;
}
.listora-btn.listora-btn--primary,
a.listora-btn.listora-btn--primary,
a.listora-btn.listora-btn--primary:visited {
	background: var(--listora-primary);
	color: var(--listora-primary-fg);
	border-color: var(--listora-primary);
}
.listora-btn.listora-btn--primary:hover,
.listora-btn.listora-btn--primary:focus,
.listora-btn.listora-btn--primary:focus-visible {
	background: var(--listora-primary-hover);
	color: var(--listora-primary-fg);
}
.listora-btn.listora-btn--secondary,
a.listora-btn.listora-btn--secondary,
a.listora-btn.listora-btn--secondary:visited {
	background: transparent;
	color: var(--listora-primary);
	border-color: var(--listora-primary);
}
.listora-btn.listora-btn--ghost,
a.listora-btn.listora-btn--ghost,
a.listora-btn.listora-btn--ghost:visited {
	background: transparent;
	color: var(--listora-fg-default);
	border-color: transparent;
}
.listora-btn.listora-btn--danger,
a.listora-btn.listora-btn--danger {
	background: var(--listora-danger);
	color: #fff;
	border-color: var(--listora-danger);
}

/* === src/components/modal.css === */
/**
 * WB Listora — .listora-modal primitive
 *
 * Canonical modal primitive. Replaces the 3 inline modals currently in
 * listing-detail/render.php (claim/share/login) plus the listoraConfirm
 * promise-modal pattern.
 *
 * Composition:
 *   <div class="listora-modal" role="dialog" aria-modal="true"
 *        aria-labelledby="claim-title" data-state="closed">
 *     <div class="listora-modal__backdrop"></div>
 *     <div class="listora-modal__panel">
 *       <header class="listora-modal__head">
 *         <h2 id="claim-title" class="listora-modal__title">Claim this business</h2>
 *         <button class="listora-btn listora-btn--icon listora-btn--ghost listora-modal__close"
 *                 aria-label="Close">×</button>
 *       </header>
 *       <div class="listora-modal__body">...</div>
 *       <footer class="listora-modal__foot">
 *         <button class="listora-btn listora-btn--ghost">Cancel</button>
 *         <button class="listora-btn listora-btn--primary">Submit</button>
 *       </footer>
 *     </div>
 *   </div>
 *
 * A11y wiring (enforced by JS controller):
 *   - role="dialog" + aria-modal="true" on root
 *   - aria-labelledby pointing at modal__title id
 *   - Focus trap inside __panel
 *   - Esc closes, backdrop click closes
 *   - Focus returns to triggering element on close
 *
 * State:
 *   data-state="open"   — visible, locks scroll
 *   data-state="closed" — hidden
 *
 * @since 1.0.5
 */

.listora-modal {
	position: fixed;
	inset: 0;
	z-index: 9999;
	display: none;
}

.listora-modal[data-state="open"] {
	display: block;
}

.listora-modal__backdrop {
	position: absolute;
	inset: 0;
	background: rgba(0, 0, 0, 0.5);
	animation: listora-modal-fade-in var(--listora-transition-base) var(--listora-ease-out);
}

.listora-modal__panel {
	position: relative;
	margin: 5vh auto;
	max-width: 600px;
	width: calc(100% - 2 * var(--listora-space-4));
	max-height: 90vh;

	background: var(--listora-bg-elevated);
	border-radius: var(--listora-modal-radius);
	box-shadow: var(--listora-modal-shadow);

	display: flex;
	flex-direction: column;
	overflow: hidden;

	animation: listora-modal-slide-in var(--listora-transition-base) var(--listora-ease-spring);
}

.listora-modal--lg .listora-modal__panel {
	max-width: 900px;
}

.listora-modal--sm .listora-modal__panel {
	max-width: 420px;
}

.listora-modal__head {
	padding: var(--listora-space-4) var(--listora-space-6);
	border-bottom: 1px solid var(--listora-border-divider);
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--listora-space-4);
	flex-shrink: 0;
}

.listora-modal__title {
	font-size: var(--listora-text-size-xl);
	font-weight: var(--listora-weight-semibold);
	color: var(--listora-fg-strong);
	margin: 0;
}

.listora-modal__close {
	flex-shrink: 0;
}

.listora-modal__body {
	padding: var(--listora-space-6);
	overflow-y: auto;
	flex: 1 1 auto;
}

.listora-modal__foot {
	padding: var(--listora-space-4) var(--listora-space-6);
	border-top: 1px solid var(--listora-border-divider);
	display: flex;
	gap: var(--listora-space-3);
	justify-content: flex-end;
	flex-wrap: wrap;
	flex-shrink: 0;
}

/* When modal is open, the body scroll is locked by JS adding this class */
body.listora-modal-open {
	overflow: hidden;
}

/* === Animations === */
@keyframes listora-modal-fade-in {
	from { opacity: 0; }
	to   { opacity: 1; }
}

@keyframes listora-modal-slide-in {
	from { opacity: 0; transform: translateY(10px) scale(0.98); }
	to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
	.listora-modal__backdrop,
	.listora-modal__panel {
		animation: none;
	}
}

/* Mobile — full screen below 480px */
@media (max-width: 480px) {
	.listora-modal__panel {
		margin: 0;
		min-height: 100vh;
		max-height: 100vh;
		width: 100%;
		border-radius: 0;
	}
}

/* === src/components/tabs.css === */
/**
 * WB Listora — .listora-tabs primitive
 *
 * Canonical tabs primitive with full ARIA wiring. Replaces:
 *   .listora-detail__tab    (listing-detail)
 *   .listora-dashboard__nav (user-dashboard)
 *
 * Composition (use wb_listora_render_tabs() helper to emit correct ARIA):
 *   <div class="listora-tabs">
 *     <div class="listora-tabs__list" role="tablist" aria-orientation="horizontal">
 *       <button class="listora-tabs__tab" role="tab"
 *               id="tab-overview" aria-selected="true"
 *               aria-controls="panel-overview" tabindex="0">
 *         Overview
 *       </button>
 *       <button class="listora-tabs__tab" role="tab"
 *               id="tab-reviews" aria-selected="false"
 *               aria-controls="panel-reviews" tabindex="-1">
 *         Reviews <span class="listora-tabs__count">12</span>
 *       </button>
 *     </div>
 *     <div class="listora-tabs__panels">
 *       <div class="listora-tabs__panel" role="tabpanel"
 *            id="panel-overview" aria-labelledby="tab-overview">...</div>
 *       <div class="listora-tabs__panel" role="tabpanel"
 *            id="panel-reviews" aria-labelledby="tab-reviews" hidden>...</div>
 *     </div>
 *   </div>
 *
 * A11y wiring (helper enforces):
 *   - role="tablist" on __list, role="tab" on each __tab, role="tabpanel" on each __panel
 *   - aria-selected + aria-controls on tabs
 *   - aria-labelledby on panels
 *   - tabindex 0 on active tab, -1 on others (roving tabindex)
 *
 * Keyboard:
 *   Left/Right arrows switch tabs, Home/End jump to first/last, Enter/Space activate.
 *
 * @since 1.0.5
 */

.listora-tabs {
	display: flex;
	flex-direction: column;
}

.listora-tabs__list {
	display: flex;
	gap: var(--listora-space-2);
	border-bottom: 1px solid var(--listora-border-divider);
	padding: 0;
	margin: 0;
	list-style: none;
	overflow-x: auto;
	scrollbar-width: thin;
}

.listora-tabs__tab {
	display: inline-flex;
	align-items: center;
	gap: var(--listora-space-2);

	padding: var(--listora-space-3) var(--listora-space-4);

	background: none;
	border: none;
	border-bottom: 2px solid transparent;
	border-radius: 0;

	color: var(--listora-fg-muted);
	font-size: var(--listora-text-size-base);
	font-weight: var(--listora-weight-medium);
	font-family: inherit;

	cursor: pointer;
	white-space: nowrap;
	transition: color var(--listora-transition-fast),
	            border-color var(--listora-transition-fast);
}

.listora-tabs__tab:hover {
	color: var(--listora-fg-default);
}

.listora-tabs__tab:focus-visible {
	outline: 2px solid transparent;
	outline-offset: 2px;
	box-shadow: var(--listora-focus-ring);
	border-radius: var(--listora-radius-sm);
}

.listora-tabs__tab[aria-selected="true"] {
	color: var(--listora-fg-strong);
	border-bottom-color: var(--listora-primary);
}

.listora-tabs__count {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 20px;
	height: 20px;
	padding: 0 var(--listora-space-1);
	border-radius: var(--listora-pill-radius);
	background: var(--listora-bg-muted);
	color: var(--listora-fg-muted);
	font-size: var(--listora-text-size-xs);
	font-weight: var(--listora-weight-semibold);
}

.listora-tabs__tab[aria-selected="true"] .listora-tabs__count {
	background: var(--listora-primary);
	color: var(--listora-primary-fg);
}

.listora-tabs__panels {
	padding-top: var(--listora-space-6);
}

.listora-tabs__panel[hidden] {
	display: none;
}

/* === Variants === */

.listora-tabs--pill .listora-tabs__list {
	border-bottom: none;
	gap: var(--listora-space-2);
}

.listora-tabs--pill .listora-tabs__tab {
	border-bottom: none;
	border-radius: var(--listora-pill-radius);
	background: var(--listora-bg-muted);
}

.listora-tabs--pill .listora-tabs__tab[aria-selected="true"] {
	background: var(--listora-primary);
	color: var(--listora-primary-fg);
}

.listora-tabs--vertical {
	flex-direction: row;
	gap: var(--listora-space-6);
}

.listora-tabs--vertical .listora-tabs__list {
	flex-direction: column;
	border-bottom: none;
	border-right: 1px solid var(--listora-border-divider);
	min-width: 200px;
}

.listora-tabs--vertical .listora-tabs__tab {
	justify-content: flex-start;
	border-bottom: none;
	border-right: 2px solid transparent;
	padding-inline-end: var(--listora-space-6);
}

.listora-tabs--vertical .listora-tabs__tab[aria-selected="true"] {
	border-right-color: var(--listora-primary);
}

/* === src/components/tooltip.css === */
/**
 * WB Listora — .listora-tooltip primitive
 *
 * Hover/focus tooltip for icon buttons and help indicators.
 *
 * Composition:
 *   <button class="listora-btn listora-btn--icon listora-btn--ghost"
 *           data-tooltip="Help text" aria-describedby="...">?</button>
 *   <span class="listora-tooltip" role="tooltip" id="..." hidden>Help text</span>
 *
 * Or as a wrapper:
 *   <span class="listora-tooltip-wrap">
 *     <button>Trigger</button>
 *     <span class="listora-tooltip" role="tooltip">Help text</span>
 *   </span>
 *
 * @since 1.0.5
 */

.listora-tooltip {
	position: absolute;
	z-index: 1000;
	padding: var(--listora-space-2) var(--listora-space-3);

	background: var(--listora-bg-inverse);
	color: var(--listora-fg-inverse);
	font-size: var(--listora-text-size-xs);
	font-weight: var(--listora-weight-medium);
	line-height: var(--listora-line-tight);

	border-radius: var(--listora-radius-sm);
	box-shadow: var(--listora-shadow-md);

	white-space: nowrap;
	pointer-events: none;
	opacity: 0;
	transition: opacity var(--listora-transition-fast);
}

.listora-tooltip[data-state="visible"] {
	opacity: 1;
}

.listora-tooltip[hidden] {
	display: none;
}

/* Wrapper for hover-based tooltips */
.listora-tooltip-wrap {
	position: relative;
	display: inline-flex;
}

.listora-tooltip-wrap:hover .listora-tooltip,
.listora-tooltip-wrap:focus-within .listora-tooltip {
	opacity: 1;
}

/* Position variants — tooltip arrow direction */
.listora-tooltip--top {
	bottom: calc(100% + var(--listora-space-2));
	left: 50%;
	transform: translateX(-50%);
}

.listora-tooltip--bottom {
	top: calc(100% + var(--listora-space-2));
	left: 50%;
	transform: translateX(-50%);
}

.listora-tooltip--left {
	right: calc(100% + var(--listora-space-2));
	top: 50%;
	transform: translateY(-50%);
}

.listora-tooltip--right {
	left: calc(100% + var(--listora-space-2));
	top: 50%;
	transform: translateY(-50%);
}

/* === src/components/table.css === */
/**
 * WB Listora — .listora-table primitive
 *
 * Canonical data-table primitive for credit history, claims list, audit log,
 * services meta box rows. Replaces per-block table inventions.
 *
 * Composition:
 *   <div class="listora-table-wrap"> (handles overflow scroll on mobile)
 *     <table class="listora-table">
 *       <thead class="listora-table__head">
 *         <tr><th>Header 1</th>...</tr>
 *       </thead>
 *       <tbody class="listora-table__body">
 *         <tr class="listora-table__row">
 *           <td class="listora-table__cell">...</td>
 *         </tr>
 *       </tbody>
 *     </table>
 *   </div>
 *
 * Variants:
 *   --bordered   (full grid lines)
 *   --striped    (alternating row backgrounds)
 *   --compact    (smaller padding)
 *
 * @since 1.0.5
 */

.listora-table-wrap {
	width: 100%;
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
}

.listora-table {
	width: 100%;
	border-collapse: collapse;
	font-size: var(--listora-text-size-sm);
	color: var(--listora-fg-default);
}

.listora-table__head {
	background: var(--listora-bg-muted);
}

.listora-table__head th,
.listora-table th {
	padding: var(--listora-space-3) var(--listora-space-4);
	text-align: left;
	font-weight: var(--listora-weight-semibold);
	color: var(--listora-fg-strong);
	font-size: var(--listora-text-size-xs);
	text-transform: uppercase;
	letter-spacing: var(--listora-tracking-wide);
	border-bottom: 1px solid var(--listora-border-default);
	white-space: nowrap;
}

.listora-table__cell,
.listora-table td {
	padding: var(--listora-space-3) var(--listora-space-4);
	border-bottom: 1px solid var(--listora-border-divider);
	vertical-align: middle;
}

.listora-table__row:last-child .listora-table__cell {
	border-bottom: none;
}

.listora-table__row:hover {
	background: color-mix(in srgb, var(--listora-primary) 4%, transparent);
}

/* === Variants === */

.listora-table--bordered,
.listora-table--bordered .listora-table__cell,
.listora-table--bordered th {
	border: 1px solid var(--listora-border-subtle);
}

.listora-table--striped .listora-table__row:nth-child(even) {
	background: var(--listora-bg-muted);
}

.listora-table--compact .listora-table__cell,
.listora-table--compact th {
	padding: var(--listora-space-2) var(--listora-space-3);
}

/* Numeric columns right-align */
.listora-table__cell--numeric,
.listora-table th[data-numeric] {
	text-align: right;
	font-variant-numeric: tabular-nums;
}

/* Status column with badge */
.listora-table__cell--status {
	text-align: center;
}

/* Empty state inside a table */
.listora-table__empty {
	padding: var(--listora-space-12) var(--listora-space-6);
	text-align: center;
	color: var(--listora-fg-muted);
}

/* === src/components/page-shell.css === */
/**
 * WB Listora — .listora-page primitive
 *
 * Canonical page-shell primitive. Wraps customer-facing page templates that
 * own their own outer chrome (listing detail, dashboard, submission, the
 * Compare Listings page).
 *
 * Composition:
 *   <div class="listora-page listora-page--single">
 *     <header class="listora-page-header">
 *       <h1 class="listora-page-header__title">...</h1>
 *       <p class="listora-page-header__desc">...</p>
 *       <div class="listora-page-header__actions">CTA buttons</div>
 *     </header>
 *     <div class="listora-page-body">
 *       (block content composed from .listora-card / .listora-tabs / etc.)
 *     </div>
 *   </div>
 *
 * Variants change only max-width + outer padding:
 *   --single    = listing detail (1200px — has sidebar)
 *   --list      = archive / search / grid (1400px)
 *   --dashboard = user dashboard (1280px)
 *   --booking   = single-purpose focused flow (720px — narrow)
 *
 * Section blocks (listing-search, listing-grid, listing-categories, etc.)
 * do NOT wrap themselves in a page shell — they're sections composed into
 * pages by the customer. Only block-owns-its-own-page blocks (detail,
 * submission, compare) apply a shell directly via render.php.
 *
 * @since 1.0.5 (extracted from shared.css in Phase 4.5)
 */

.listora-page-shell,
.listora-page--single,
.listora-page--list,
.listora-page--dashboard,
.listora-page--booking {
	margin-inline: auto;
	padding: var(--listora-page-padding-y) var(--listora-page-padding-x);
	display: flex;
	flex-direction: column;
	gap: var(--listora-space-6);
}

.listora-page--single    { max-width: var(--listora-page-max-single); }
.listora-page--list      { max-width: var(--listora-page-max-list); }
.listora-page--dashboard { max-width: var(--listora-page-max-dashboard); }
.listora-page--booking   { max-width: var(--listora-page-max-booking); }

.listora-page-header {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--listora-space-4);
}

.listora-page-header__title {
	font-family: inherit;
	font-size: var(--listora-text-size-2xl);
	font-weight: var(--listora-weight-semibold);
	line-height: 1.2;
	margin: 0;
	color: var(--listora-fg-default);
}

.listora-page-header__desc {
	font-size: var(--listora-text-size-sm);
	color: var(--listora-fg-muted);
	margin: var(--listora-space-1) 0 0;
}

.listora-page-header__actions {
	display: inline-flex;
	gap: var(--listora-space-2);
	flex-wrap: wrap;
	align-items: center;
}

.listora-page-body {
	display: flex;
	flex-direction: column;
	gap: var(--listora-space-6);
	min-width: 0; /* prevent grid blowout */
}

/* Mobile: reduce outer padding to keep content edge-to-edge friendly. */
@media (max-width: 640px) {
	.listora-page-shell,
	.listora-page--single,
	.listora-page--list,
	.listora-page--dashboard,
	.listora-page--booking {
		padding-inline: var(--listora-space-4);
	}
}

/* === src/components/card.css === */
/**
 * WB Listora — .listora-card primitive
 *
 * Canonical card primitive. The two block-local card classes that existed
 * before Phase 4.5 (.listora-card in listing-card style.css, .listora-card
 * slot anchors in shared.css) have been retired into this single file.
 *
 * Composition:
 *   <article class="listora-card">
 *     <div class="listora-card__media">...image or media slot...</div>
 *     <div class="listora-card__body">
 *       <h3 class="listora-card__title">Card title</h3>
 *       <p class="listora-card__excerpt">Card content</p>
 *     </div>
 *     <div class="listora-card__head">...optional head slot, used at top of card...</div>
 *     <div class="listora-card__foot">...optional foot slot, used at bottom...</div>
 *   </article>
 *
 * Variants (see card.css in listing-card block for listing-specific extensions):
 *   --empty       — empty-state card (paired with .listora-empty inside)
 *   --horizontal  — image left, body right (~220px image)
 *   --compact     — minimal padding for dense lists
 *   --clickable   — pointer cursor + hover lift
 *
 * Phase 4.5 scope: only the slot anchors live here. The full chrome
 * (background, border, radius, shadow, hover transitions, image aspect
 * ratio, container queries) still lives in listing-card/style.css because
 * the existing implementation is heavily customized. Future Phase 4.6 work
 * will lift the universal chrome here and reduce listing-card to
 * listing-specific extensions only.
 *
 * @since 1.0.5
 */

/* .listora-card__head + __body + __foot — slot anchors when a block opts
 * into the chrome+slot pattern. listing-card uses these selectors today;
 * other blocks compose around .listora-card directly without slots. */

.listora-card__head {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--listora-space-3);
	padding: var(--listora-space-4) var(--listora-space-5);
	border-bottom: 1px solid var(--listora-border-subtle);
}

.listora-card__foot {
	padding: var(--listora-space-4) var(--listora-space-5);
	border-top: 1px solid var(--listora-border-subtle);
	display: flex;
	flex-wrap: wrap;
	gap: var(--listora-space-2);
	align-items: center;
	justify-content: flex-end;
}

/* Mobile: tighten head/foot inline padding. */
@media (max-width: 640px) {
	.listora-card__head,
	.listora-card__foot {
		padding-inline: var(--listora-space-4);
	}
}

/* === src/components/empty-state.css === */
/**
 * WB Listora — .listora-empty primitive
 *
 * Canonical empty-state primitive. Used by listing-grid, listing-categories,
 * listing-reviews when result count = 0. Also used by user-dashboard tabs
 * (Phase 4.6 follow-up — currently each tab inlines its own empty markup).
 *
 * Composition (use wb_listora_render_empty_state() helper):
 *   <div class="listora-card listora-card--empty">
 *     <div class="listora-empty">
 *       <div class="listora-empty__icon">{lucide-icon}</div>
 *       <h3 class="listora-empty__title">No listings found</h3>
 *       <p class="listora-empty__desc">Try removing some filters or searching for something else.</p>
 *       <div class="listora-empty__actions">
 *         <a class="listora-btn listora-btn--primary" href="/listings/">Clear filters</a>
 *       </div>
 *     </div>
 *   </div>
 *
 * The .listora-card--empty variant pairs .listora-card chrome (border, radius,
 * shadow from the canonical card primitive) with empty-specific defaults
 * (240px min-height, centered axis).
 *
 * @since 1.0.5 (extracted from shared.css in Phase 4.5)
 */

/* Card variant: empty-state container. Pairs with .listora-empty inside. */
/* Premium-feel baseline. The icon sits in a softly-tinted primary
 * circle (NOT plain grey) so empty states read as "branded surface,
 * intentional" rather than "broken / empty". Subtle entrance rise
 * fades the surface in over 220ms — respects prefers-reduced-motion. */
.listora-card--empty {
	min-height: 280px;
	align-items: center;
	justify-content: center;
}

.listora-empty {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--listora-space-3);
	padding: var(--listora-space-10) var(--listora-space-5);
	text-align: center;
	max-width: 440px;
	margin-inline: auto;
	animation: listora-empty-rise 220ms ease-out both;
}

.listora-empty__icon {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 64px;
	height: 64px;
	border-radius: 50%;
	background:
		radial-gradient(
			circle at 30% 30%,
			color-mix(in srgb, var(--listora-primary) 14%, transparent) 0%,
			color-mix(in srgb, var(--listora-primary) 6%, transparent) 60%,
			transparent 100%
		),
		var(--listora-bg-elevated);
	color: var(--listora-primary);
	margin-bottom: var(--listora-space-3);
	box-shadow:
		inset 0 0 0 1px color-mix(in srgb, var(--listora-primary) 12%, transparent),
		0 1px 3px color-mix(in srgb, var(--listora-primary) 10%, transparent);
}

.listora-empty__icon svg {
	width: 28px;
	height: 28px;
	stroke-width: 1.75;
}

.listora-empty__title {
	font-family: inherit;
	font-size: var(--listora-text-size-xl);
	font-weight: var(--listora-weight-semibold);
	line-height: 1.3;
	margin: 0;
	color: var(--listora-fg-default);
	letter-spacing: -0.01em;
}

.listora-empty__desc {
	font-size: var(--listora-text-size-base);
	color: var(--listora-fg-muted);
	margin: 0;
	line-height: 1.55;
	max-width: 380px;
}

.listora-empty__actions {
	display: inline-flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: var(--listora-space-3);
	margin-top: var(--listora-space-3);
}

@keyframes listora-empty-rise {
	from {
		opacity: 0;
		transform: translateY(4px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}

@media (prefers-reduced-motion: reduce) {
	.listora-empty {
		animation: none;
	}
}

/* === src/components/badge.css === */
/**
 * WB Listora — .listora-badge primitive
 *
 * Canonical badge primitive — small text pill used for status indicators,
 * type labels, featured markers, "open/closed" indicators. Used by every
 * block that shows a status (listing-card, dashboard rows, claims list,
 * reviews list, Pro extensions).
 *
 * Composition:
 *   <span class="listora-badge listora-badge--success">Approved</span>
 *   <span class="listora-badge listora-badge--type">Restaurant</span>
 *   <span class="listora-badge listora-badge--featured">Featured</span>
 *
 * Variants:
 *   --success / --warning / --danger / --info / --neutral
 *     (semantic status colors — use these first)
 *   --type
 *     (uses --listora-type-color set per-listing-type, falls back to primary)
 *   --featured
 *     (gold gradient, used on featured listings)
 *   --open / --closed
 *     (business-hours derived status)
 *
 * @since 1.0.5 (extracted from shared.css in Phase 4.5)
 */

.listora-badge {
	display: inline-flex;
	align-items: center;
	gap: 0.25em;
	padding: 0.15em 0.5em;
	font-size: var(--listora-card-meta-size);
	font-weight: var(--listora-weight-medium);
	line-height: 1.4;
	border-radius: var(--listora-radius-sm);
	white-space: nowrap;
}

/* === Semantic variants === */

.listora-badge--success {
	background: var(--listora-success-bg);
	color: var(--listora-success-fg);
}

.listora-badge--warning {
	background: var(--listora-warning-bg);
	color: var(--listora-warning-fg);
}

.listora-badge--danger {
	background: var(--listora-danger-bg);
	color: var(--listora-danger-fg);
}

.listora-badge--info {
	background: var(--listora-info-bg);
	color: var(--listora-info);
}

.listora-badge--neutral {
	background: var(--listora-bg-muted);
	color: var(--listora-fg-muted);
}

/* === Domain-specific variants === */

.listora-badge--type {
	background: color-mix(in srgb, var(--listora-type-color, var(--listora-primary)) 12%, transparent);
	color: var(--listora-type-color, var(--listora-primary));
}

.listora-badge--featured {
	background: linear-gradient(135deg, var(--listora-rating), color-mix(in srgb, var(--listora-rating) 80%, var(--listora-warning)));
	color: var(--listora-fg-inverse, #ffffff);
	font-weight: 600;
	text-shadow: 0 1px 2px rgba(0,0,0,0.15);
	letter-spacing: 0.02em;
}

.listora-badge--open {
	background: color-mix(in srgb, var(--listora-success) 12%, transparent);
	color: var(--listora-success);
}

.listora-badge--closed {
	background: color-mix(in srgb, var(--listora-danger) 10%, transparent);
	color: var(--listora-danger);
}

/* === src/components/admin-settings-layout.css === */
/**
 * WB Listora — Admin Header + Card primitives (F4 + F6, Jetonomy Pattern A)
 *
 * F4 .listora-admin-header — branded top-of-page header for every admin page.
 * F6 .listora-settings-card — card wrapper for sections inside .listora-settings-content.
 *
 * F5 (sidebar-nav layout) lives in assets/css/admin/settings.css under the
 * canonical .listora-settings-wrap / .listora-settings-sidebar / .listora-settings-nav-item
 * vocabulary — it predates the v2 primitive directory.
 *
 * Markup contract:
 *
 *   <div class="wrap wb-listora-admin">
 *       <!-- F4 -->
 *       <div class="listora-admin-header">
 *           <div class="listora-admin-header__brand">
 *               <span class="dashicons … listora-admin-header__icon"></span>
 *               <div class="listora-admin-header__text">
 *                   <p class="listora-admin-header__title">…</p>
 *                   <p class="listora-admin-header__sub">…</p>
 *               </div>
 *           </div>
 *           <div class="listora-admin-header__actions">…</div>
 *       </div>
 *
 *       <!-- F5 (existing) — .listora-settings-wrap from admin/settings.css -->
 *
 *           <!-- F6 (within .listora-settings-content) -->
 *           <div class="listora-settings-card">
 *               <div class="listora-settings-card__head">
 *                   <p class="listora-settings-card__title">…</p>
 *                   <p class="listora-settings-card__desc">…</p>
 *               </div>
 *               <table class="form-table">…</table>
 *           </div>
 *
 *           <!-- F6 auto variant (wraps legacy <h2>+.form-table without template changes) -->
 *           <div class="listora-settings-card--auto">
 *               <h2>…</h2>
 *               <p>…</p>
 *               <table class="form-table">…</table>
 *           </div>
 *
 *   </div>
 *
 * @since Pass 3 R-F4F5F6 (Foundation Cleanup 2026-05-11)
 * @package WBListora
 */

/* ═══════════════════════════════════════════════════════════
   F4 — Branded admin header
   ═══════════════════════════════════════════════════════════ */

.listora-admin-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--listora-space-4);
	padding: var(--listora-space-4) var(--listora-space-6);
	margin: 0 0 var(--listora-space-5);
	background: var(--listora-bg-elevated);
	border: 1px solid var(--listora-border-subtle);
	border-radius: var(--listora-radius-md);
	box-shadow: var(--listora-shadow-sm);
}

.listora-admin-header__brand {
	display: flex;
	align-items: center;
	gap: var(--listora-space-3);
	min-width: 0;
}

.listora-admin-header__icon {
	font-size: 22px;
	width: 22px;
	height: 22px;
	color: var(--listora-primary);
	flex-shrink: 0;
}

.listora-admin-header__text {
	min-width: 0;
}

.listora-admin-header__title {
	margin: 0;
	font-size: var(--listora-text-size-lg);
	font-weight: var(--listora-weight-bold);
	color: var(--listora-wp-admin-text);
	line-height: var(--listora-line-tight);
}

.listora-admin-header__sub {
	margin: 2px 0 0;
	font-size: var(--listora-text-size-xs);
	color: var(--listora-wp-admin-text-faint);
	text-transform: uppercase;
	letter-spacing: 0.06em;
	font-weight: var(--listora-weight-medium);
}

.listora-admin-header__actions {
	display: flex;
	align-items: center;
	gap: var(--listora-space-2);
	flex-shrink: 0;
}

@media (max-width: 1024px) {
	.listora-admin-header {
		flex-wrap: wrap;
	}
}

/* ═══════════════════════════════════════════════════════════
   F6 — Settings card primitive (canonical)
   ═══════════════════════════════════════════════════════════ */

.listora-settings-card {
	background: var(--listora-bg-elevated);
	border: 1px solid var(--listora-border-subtle);
	border-radius: var(--listora-radius-md);
	box-shadow: var(--listora-shadow-sm);
	margin-bottom: var(--listora-space-5);
	overflow: hidden;
}

.listora-settings-card__head {
	padding: var(--listora-space-3) var(--listora-space-5);
	border-bottom: 1px solid var(--listora-border-subtle);
	background: var(--listora-admin-bg-subtle);
}

.listora-settings-card__title {
	font-size: var(--listora-text-size-sm);
	font-weight: var(--listora-weight-bold);
	color: var(--listora-wp-admin-text);
	margin: 0 0 2px;
	text-transform: uppercase;
	letter-spacing: 0.04em;
}

.listora-settings-card__desc {
	font-size: var(--listora-text-size-sm);
	color: var(--listora-wp-admin-text-muted);
	margin: 0;
	line-height: var(--listora-line-base);
}

/* Form-table reset inside cards */
.listora-settings-card .form-table,
.listora-settings-card--auto .form-table {
	margin: 0;
}

.listora-settings-card .form-table th,
.listora-settings-card--auto .form-table th {
	padding: var(--listora-space-3) var(--listora-space-5);
	font-size: var(--listora-text-size-sm);
	font-weight: var(--listora-weight-medium);
	color: var(--listora-wp-admin-text-muted);
	width: 220px;
	vertical-align: middle;
}

.listora-settings-card .form-table td,
.listora-settings-card--auto .form-table td {
	padding: var(--listora-space-3) var(--listora-space-5);
}

.listora-settings-card .form-table tr,
.listora-settings-card--auto .form-table tr {
	border-bottom: 1px solid var(--listora-admin-bg-soft);
}

.listora-settings-card .form-table tr:last-child,
.listora-settings-card--auto .form-table tr:last-child {
	border-bottom: none;
}

.listora-settings-card .submit,
.listora-settings-card--auto .submit {
	padding: var(--listora-space-3) var(--listora-space-5);
	border-top: 1px solid var(--listora-admin-bg-soft);
	margin: 0;
}

/* ── F6 auto-card variant ──
   Wraps legacy <h2> + .form-table siblings into a card without requiring
   template changes. Used by extension/Pro tabs that emit Settings-API
   output without manually wrapping each section. Any direct child
   <h2>/<h3>/<p> styles as a card head + body. */
.listora-settings-card--auto {
	background: var(--listora-bg-elevated);
	border: 1px solid var(--listora-border-subtle);
	border-radius: var(--listora-radius-md);
	box-shadow: var(--listora-shadow-sm);
	margin-bottom: var(--listora-space-5);
	overflow: hidden;
}

.listora-settings-card--auto > h2,
.listora-settings-card--auto > h3 {
	padding: var(--listora-space-3) var(--listora-space-5);
	border-bottom: 1px solid var(--listora-border-subtle);
	margin: 0;
	font-size: var(--listora-text-size-sm);
	font-weight: var(--listora-weight-bold);
	color: var(--listora-wp-admin-text);
	background: var(--listora-admin-bg-subtle);
}

.listora-settings-card--auto > p {
	padding: var(--listora-space-2) var(--listora-space-5) 0;
	margin: 0;
	color: var(--listora-wp-admin-text-muted);
	font-size: var(--listora-text-size-sm);
}

/* === src/shared/theme-isolation.css === */
/**
 * WB Listora — theme-defence: anchor neutralization inside Listora wrappers.
 *
 * Block-ancestor scoped, specificity-only (NO !important). 0,5,1 beats
 * aggressive themes' .entry-content a:not()x3 (0,4,1). Built into
 * assets/css/listora-components.css by bin/build-css.mjs.
 *
 * @package WBListora
 */

/* ============================================================
 * Theme-defence: anchor neutralization inside Listora wrappers
 * ============================================================
 *
 * Themes (BuddyX, Astra, Reign, GeneratePress, Hello Elementor,
 * Twenty Twenty-*) ship aggressive `.entry-content a` rules that
 * paint every anchor in the theme accent + add an underline. Inside
 * Listora blocks we want plain anchors to read as ordinary text
 * (color: inherit) and gain the underline ONLY on hover/focus.
 *
 * Selector specificity 0,2,1 — matches modern themes'
 * `.entry-content a:not(.x)` exactly. Source-order tiebreak resolves
 * in Listora's favour because the plugin stylesheet loads after the
 * theme. Exclusions protect Listora's own styled link primitives
 * (.listora-btn, .listora-*__link, .listora-link) so they keep
 * their variant colors.
 * ============================================================ */

/* [class*=] (contains), not [class^=] (starts-with). Listora block wrappers
 * compose multiple classes (e.g. "listora-grid-wrapper listora-block
 * wp-block-listora-listing-grid") so wp-block-listora-* is rarely at index 0.
 * Use [class*=] to match wherever it lands in the class string. The fourth
 * :not() (.wp-element-button) gates Listora's own button anchors so they
 * keep variant chrome. Specificity 0,5,1 — beats BuddyX's
 * .entry-content a:not()×3 at 0,4,1 unconditionally. */
[class*="wp-block-listora"] a:not([class*="listora-btn"]):not([class*="__link"]):not(.listora-link):not(.wp-element-button):not([class*="page-num"]),
.entry-content [class*="wp-block-listora"] a:not([class*="listora-btn"]):not([class*="__link"]):not(.listora-link):not(.wp-element-button):not([class*="page-num"]) {
	color: inherit;
	text-decoration: none;
}

[class*="wp-block-listora"] a:not([class*="listora-btn"]):not([class*="__link"]):not(.listora-link):not(.wp-element-button):not([class*="page-num"]):hover,
[class*="wp-block-listora"] a:not([class*="listora-btn"]):not([class*="__link"]):not(.listora-link):not(.wp-element-button):not([class*="page-num"]):focus-visible {
	color: var(--listora-primary);
	text-decoration: underline;
	text-underline-offset: 3px;
	text-decoration-thickness: 1.5px;
}

/* === src/components/theme-hardening.css === */
/**
 * WB Listora — component controls (icon buttons, nav, pagination).
 *
 * The base `.listora-btn` + variants live in button.css. This file styles the
 * component-scoped controls button.css doesn't cover. Theme-independent via
 * doubled-class specificity (e.g. `.listora-favorite-btn.listora-favorite-btn`)
 * — NO !important — which outranks themes' generic element resets.
 *
 * Built into assets/css/listora-components.css by bin/build-css.mjs.
 *
 * @package WBListora
 */

/* ============================================================
 * Theme-bridge — minimum tap targets for content-area links
 * ----------------------------------------------------------------
 * Mobile UX audit 2026-05-25: BuddyX (and similar themes) style
 * bare `<a class="button">` in plugin content at 26px height
 * (their .button class is link-typography sized). Inside Listora
 * detail / dashboard surfaces those are customer-facing CTAs that
 * MUST meet the 44px tap-target floor on touch. Bridge here with
 * doubled-class specificity on the surrounding Listora container
 * so we win over the theme without !important.
 * ============================================================ */
.listora-detail.listora-detail a.button,
.listora-detail.listora-detail a.btn-button,
.listora-dashboard.listora-dashboard a.button {
	min-height: var(--listora-tap-target, 44px);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding-inline: var(--listora-space-4, 1rem);
}

/* ============================================================
 * Component controls — icon buttons, nav, pagination
 * ============================================================ */

/* Plugin icon-buttons (favorite, quick-view, near-me, gallery nav, etc.):
   subtle elevated circular controls — never the theme's solid-primary skin.
   Mobile UX audit 2026-05-25: enforce 40px tap-target floor here with
   doubled-class specificity so theme card-grid resets can't shrink these
   below the touch threshold. */
.listora-card .listora-favorite-btn.listora-favorite-btn,
.listora-card .listora-quick-view-btn.listora-quick-view-btn,
.listora-search__near-me.listora-search__near-me {
	background: var(--listora-bg-elevated, #fff);
	color: var(--listora-fg-muted, #555);
	border: 1px solid var(--listora-border-subtle, rgba(0, 0, 0, 0.1));
	border-radius: 9999px;
	box-shadow: none;
	min-width: var(--listora-tap-target, 40px);
	min-height: var(--listora-tap-target, 40px);
}

.listora-card .listora-favorite-btn:hover,
.listora-card .listora-quick-view-btn:hover {
	background: var(--listora-primary);
	color: var(--listora-primary-fg);
	border-color: var(--listora-primary);
}

/* ============================================================
 * Component buttons — review helpful/report, modal close, gallery thumb.
 * These are NOT primary CTAs; they keep their subtle/outline treatment.
 * Doubled-class specificity outranks themes' generic resets (no !important).
 * ============================================================ */

/* Review "Helpful" / "Report" — subtle outline pills, never a solid fill. */
.listora-detail__review-helpful-btn.listora-detail__review-helpful-btn,
.listora-reviews__helpful-btn.listora-reviews__helpful-btn,
.listora-reviews__report-btn.listora-reviews__report-btn {
	background: transparent;
	color: var(--listora-fg-muted, #555);
	border: 1px solid var(--listora-border-subtle, rgba(0, 0, 0, 0.12));
	border-radius: 9999px;
	box-shadow: none;
	text-shadow: none;
}
.listora-detail__review-helpful-btn.listora-detail__review-helpful-btn:hover,
.listora-reviews__helpful-btn.listora-reviews__helpful-btn:hover,
.listora-reviews__report-btn.listora-reviews__report-btn:hover {
	border-color: var(--listora-primary);
	color: var(--listora-primary);
	background: transparent;
}

/* Modal close buttons — subtle neutral circle, dark glyph (not a red square).
   Mobile UX audit 2026-05-25: enforce 44px tap-target floor (modal closes
   need the larger 44px size since they're a deliberate dismiss action). */
.listora-detail__modal-close.listora-detail__modal-close,
.listora-dashboard__renew-modal-close.listora-dashboard__renew-modal-close {
	background: var(--listora-bg-muted, rgba(0, 0, 0, 0.06));
	color: var(--listora-fg-default, #1a1a1a);
	border: none;
	border-radius: 9999px;
	box-shadow: none;
	min-width: var(--listora-tap-target, 44px);
	min-height: var(--listora-tap-target, 44px);
}
.listora-detail__modal-close.listora-detail__modal-close:hover,
.listora-dashboard__renew-modal-close.listora-dashboard__renew-modal-close:hover {
	background: var(--listora-bg-muted-hover, rgba(0, 0, 0, 0.12));
}

/* Gallery thumbnail — transparent tile with a neutral border; primary when active. */
.listora-detail__gallery-thumb.listora-detail__gallery-thumb {
	background: transparent;
	border: 2px solid var(--listora-border-subtle, rgba(0, 0, 0, 0.12));
	border-radius: var(--listora-radius-md, 8px);
	box-shadow: none;
}
.listora-detail__gallery-thumb.listora-detail__gallery-thumb.is-active {
	border-color: var(--listora-primary);
}

/* ============================================================
 * Component buttons, part 2 — icon-nav, carousel dots, tabs / view-toggles /
 * dashboard nav. Subtle/outline treatments held by doubled-class specificity.
 * ============================================================ */

/* Subtle circular icon-nav buttons (search clear, calendar + featured + grid nav). */
.listora-search__clear.listora-search__clear,
.listora-calendar__nav-btn.listora-calendar__nav-btn,
.listora-featured__arrow.listora-featured__arrow {
	background: var(--listora-bg-elevated, #fff);
	color: var(--listora-fg-muted, #555);
	border: 1px solid var(--listora-border-subtle, rgba(0, 0, 0, 0.1));
	border-radius: 9999px;
	box-shadow: none;
}
.listora-search__clear.listora-search__clear:hover,
.listora-calendar__nav-btn.listora-calendar__nav-btn:hover,
.listora-featured__arrow.listora-featured__arrow:hover {
	background: var(--listora-primary);
	color: var(--listora-primary-fg);
	border-color: var(--listora-primary);
}

/* Carousel dots — neutral pill, primary when active. */
.listora-featured__dot.listora-featured__dot {
	background: var(--listora-border, rgba(0, 0, 0, 0.2));
	border: none;
	border-radius: 9999px;
	box-shadow: none;
}
.listora-featured__dot.listora-featured__dot.is-active {
	background: var(--listora-primary);
}

/* Tabs / view-toggles / dashboard nav — transparent base; primary-tint active. */
.listora-search__type-tab.listora-search__type-tab,
.listora-grid__view-btn.listora-grid__view-btn,
.listora-dashboard__nav-item.listora-dashboard__nav-item,
.listora-dashboard__menu-item.listora-dashboard__menu-item {
	background: transparent;
	color: var(--listora-fg-default, #1a1a1a);
	border-color: var(--listora-border-subtle, rgba(0, 0, 0, 0.1));
	box-shadow: none;
	text-shadow: none;
}
.listora-search__type-tab.listora-search__type-tab.is-active,
.listora-grid__view-btn.listora-grid__view-btn.is-active,
.listora-dashboard__nav-item.listora-dashboard__nav-item.is-active {
	background: color-mix(in srgb, var(--listora-primary) 10%, transparent);
	color: var(--listora-primary);
	border-color: var(--listora-primary);
}
.listora-dashboard__menu-item--danger.listora-dashboard__menu-item--danger {
	color: var(--listora-danger, #dc2626);
}

/* ============================================================
 * UNIFORM MODERN PAGINATION (since 1.0.5, Phase 2)
 * One numbered-pagination treatment across every surface — the grid's custom
 * classes AND WP-core paginate_links() output (`.page-numbers`, used by the Pro
 * needs grid). Current page = primary fill; others = subtle outline; nav = icon.
 * Theme-independent via doubled-class + block-ancestor specificity (no
 * !important); pagination anchors are excluded from theme-isolation's reset.
 * ============================================================ */
.listora-grid__page-num.listora-grid__page-num,
[class*="wp-block-listora"] a.page-numbers,
[class*="wp-block-listora"] span.page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 40px;
	height: 40px;
	padding: 0 0.6rem;
	background: transparent;
	color: var(--listora-fg-default, #1a1a1a);
	border: 1px solid var(--listora-border-subtle, rgba(0, 0, 0, 0.12));
	border-radius: var(--listora-radius-md, 8px);
	font-weight: var(--listora-weight-medium, 500);
	text-decoration: none;
	box-shadow: none;
	text-shadow: none;
}
.listora-grid__page-num.listora-grid__page-num:hover,
[class*="wp-block-listora"] a.page-numbers:hover {
	background: color-mix(in srgb, var(--listora-primary) 10%, transparent);
	border-color: var(--listora-primary);
	color: var(--listora-primary);
}
.listora-grid__page-num.listora-grid__page-num.is-active,
.listora-grid__page-num.listora-grid__page-num[aria-current="page"],
[class*="wp-block-listora"] span.page-numbers.current {
	background: var(--listora-primary);
	color: var(--listora-primary-fg, #fff);
	border-color: var(--listora-primary);
}
/* Ellipsis / dots — plain muted text, no button chrome. */
.listora-grid__page-ellipsis,
[class*="wp-block-listora"] span.page-numbers.dots {
	display: inline-flex;
	align-items: center;
	min-width: auto;
	background: transparent;
	border: none;
	color: var(--listora-fg-muted, #6b7280);
}

