/* BuddyNext — Feed component styles.
 *
 * Covers: post card, reaction bar, poll, link preview, share embed,
 * announcement card, content warning overlay, feed layout, composer,
 * skeleton loader, sidebar widgets. Dark mode via [data-theme="dark"].
 * Mobile responsive at ≤640px.
 */

/* All --bn-* tokens are defined canonically in bn-base.css. The legacy
 * :root alias block that used to live here (--bn-s4: var(--s4) etc.)
 * has been removed — it created circular references when other CSS
 * files defined --s4 themselves (legacy alias on the OTHER side), and
 * the resulting alias loop collapsed every spacing token to 0px on any
 * hub that loaded bn-feed.css alongside another stylesheet.
 *
 * If a rule below references a legacy --bn-* name that no longer exists,
 * it should be updated to use the canonical --bn-* token from bn-base.css. */

/* ── Feed page shell ────────────────────────────────────────────────────────── */
.bn-feed-page {
	font-family: var(--bn-font-body);
	font-size: var(--bn-text-base);
	line-height: var(--bn-leading-body);
	color: var(--bn-text-1);
	background: var(--bn-bg-subtle);
	-webkit-font-smoothing: antialiased;
	min-height: 100vh;
}

/* Feed stack: vertical rhythm for the inner main column.
   The shell owns the 2-column grid; this rule owns inner spacing. */
.bn-feed-stack {
	display: flex;
	flex-direction: column;
	/* 20px between cards: the 1px --bn-line border on a light surface reads as
	   cramped at 16px, so give the feed a touch more premium breathing room. */
	gap: var(--bn-s5);
}

/* ── Post card (v2 pattern) ─────────────────────────────────────────────────────
   Source: docs/v2 Plans/v2/home-feed.html post sections + post-detail.html.
   Single border, no shadow at rest, padding `--bn-s5`, gap `--bn-s4` between
   regions. Theme-agnostic — no BuddyX/Reign/Astra-specific selectors.
   The card owns its own typography to insulate against community-theme bleed. */
.bn-post-card {
	font-family: var(--bn-font-ui);
	font-size: var(--bn-text-base);
	line-height: 1.6;
	color: var(--bn-ink);
	background: var(--bn-surface);
	border: 1px solid var(--bn-line);
	border-radius: var(--bn-r-lg);
	padding: var(--bn-s5);
	display: flex;
	flex-direction: column;
	gap: var(--bn-s4);
	/* Spacing between cards comes from the parent .bn-feed-stack { gap: var(--bn-s4) }.
	 * Don't add margin-block-end here — that would double-space. */
	transition: border-color var(--bn-dur, 200ms) var(--bn-ease, ease);
}

.bn-post-card:hover {
	border-color: var(--bn-line-strong);
}

/* Announcement + pinned variants — subtle accent rails on the v2 single border. */
.bn-post-card--announcement {
	border-inline-start-width: 4px;
	border-inline-start-color: var(--bn-accent);
}

.bn-post-card--pinned {
	border-block-start-width: 3px;
	border-block-start-color: var(--bn-warn);
}

/* Announcement / pin banners — flush rows above the head. */
.bn-post-card__announcement-bar {
	display: flex;
	align-items: center;
	/* gap separates the End action from the × close; the badge takes the free
	   space (margin-inline-end: auto below) so End + × cluster at the trailing
	   edge instead of End floating in the middle (space-between did that). */
	gap: var(--bn-s2);
	background: var(--bn-accent-100);
	padding: var(--bn-s2) var(--bn-s3);
	border-radius: var(--bn-r-md);
	margin-block-end: calc(var(--bn-s4) * -1 + var(--bn-s2));
}

.bn-post-card__ann-badge {
	display: inline-flex;
	align-items: center;
	gap: var(--bn-s1);
	/* Push the trailing controls (End, ×) to the right edge. */
	margin-inline-end: auto;
	font-size: var(--bn-text-xs);
	font-weight: 700;
	color: var(--bn-accent-700);
	text-transform: uppercase;
	letter-spacing: 0.06em;
}

.bn-post-card__ann-dismiss {
	background: transparent;
	border: 0;
	color: var(--bn-ink-3);
	cursor: pointer;
	line-height: 1;
	padding: 2px var(--bn-s1);
	border-radius: var(--bn-r-sm);
	transition: color var(--bn-dur-fast, 120ms) var(--bn-ease, ease), background var(--bn-dur-fast, 120ms) var(--bn-ease, ease);
}

.bn-post-card__ann-dismiss:hover {
	color: var(--bn-danger);
	background: var(--bn-danger-bg);
}

.bn-post-card__pin-label {
	display: inline-flex;
	align-items: center;
	gap: var(--bn-s1);
	font-size: var(--bn-text-xs);
	font-weight: 600;
	color: var(--bn-warn);
	background: var(--bn-warn-bg);
	padding: var(--bn-s1) var(--bn-s3);
	border-radius: var(--bn-r-md);
	width: max-content;
	margin-block-end: calc(var(--bn-s4) * -1 + var(--bn-s2));
}

/* Head row: avatar + author block + kebab (v2 .post-head). */
.bn-post-card__head {
	display: flex;
	align-items: center;
	gap: var(--bn-s3);
}

.bn-post-card__avatar-link {
	display: inline-flex;
	flex-shrink: 0;
}

.bn-post-card__author-block {
	flex: 1;
	min-width: 0;
}

/* Byline Follow — a compact pill pinned to the right of the head row, beside
   the options kebab (LinkedIn/X/Facebook pattern). The author block (flex:1)
   absorbs the remaining width, so this never forces a dead extra row beneath
   the card head. flex-shrink:0 so the pill keeps its size on narrow cards. */
.bn-post-card__follow {
	display: inline-flex;
	align-items: center;
	flex-shrink: 0;
}
.bn-post-card__follow .bn-follow-btn,
.bn-post-card__follow .bn-btn {
	min-height: 0;
	height: auto;
	padding: 4px 14px;
	font-size: var(--bn-text-xs);
	font-weight: 600;
	border-radius: var(--bn-r-full, 999px);
	line-height: 1.4;
}

.bn-post-card__author {
	display: flex;
	align-items: center;
	gap: var(--bn-s2);
	flex-wrap: wrap;
	font-size: var(--bn-text-sm);
	font-weight: 600;
	color: var(--bn-ink);
	line-height: 1.3;
}

.bn-post-card__author-name {
	color: var(--bn-ink);
	text-decoration: none;
	font-weight: 600;
}

.bn-post-card__author-name:hover { color: var(--bn-accent); }

.bn-post-card__member-type {
	text-transform: uppercase;
	letter-spacing: 0.06em;
}

/* Connection-degree pill (1st / 2nd) — matches parts/member-card's
   .bn-md-card__degree so the byline and directory read consistently. */
.bn-post-card__degree {
	font-family: var(--bn-font-mono, var(--bn-font-ui));
	font-size: 10px;
	font-weight: var(--bn-fw-semibold, 600);
	letter-spacing: 0.04em;
	line-height: 1.2;
	padding: 2px 6px;
	border-radius: 999px;
	color: var(--bn-accent);
	background: color-mix(in oklch, var(--bn-accent) 14%, var(--bn-surface));
	border: 1px solid color-mix(in oklch, var(--bn-accent) 28%, transparent);
}
.bn-post-card__degree[data-degree="2"] {
	color: var(--bn-ink-2);
	background: var(--bn-sunken);
	border-color: var(--bn-line);
}

.bn-post-card__meta {
	display: flex;
	align-items: center;
	gap: var(--bn-s1);
	flex-wrap: wrap;
	margin-block-start: 2px;
	font-size: var(--bn-text-xs);
	color: var(--bn-ink-3);
}

.bn-post-card__handle {
	color: var(--bn-ink-2);
	font-weight: 500;
	text-decoration: none;
}

.bn-post-card__handle:hover { color: var(--bn-accent); }

.bn-post-card__space-link {
	display: inline-flex;
	align-items: center;
	gap: var(--bn-s1);
	color: var(--bn-ink-2);
	font-weight: 500;
	text-decoration: none;
}
.bn-post-card__space-link:hover { color: var(--bn-accent); }
.bn-post-card__space-link .bn-icon {
	width: 12px;
	height: 12px;
}

.bn-post-card__sep {
	color: var(--bn-ink-4);
}

.bn-post-card__time,
.bn-post-card__edited {
	color: var(--bn-ink-3);
}

.bn-post-card__edited {
	font-style: italic;
}

.bn-post-card__privacy {
	display: inline-flex;
	align-items: center;
	gap: var(--bn-s1);
	color: var(--bn-ink-3);
}

.bn-post-card__privacy svg {
	width: 12px;
	height: 12px;
}

/* Kebab menu trigger + dropdown. */
.bn-post-card__menu-wrap { position: relative; flex-shrink: 0; }

.bn-post-card__menu {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	padding: 0;
	background: transparent;
	border: 0;
	border-radius: var(--bn-r-md);
	color: var(--bn-ink-3);
	cursor: pointer;
	transition: background var(--bn-dur-fast, 120ms) var(--bn-ease, ease), color var(--bn-dur-fast, 120ms) var(--bn-ease, ease);
}

.bn-post-card__menu:hover {
	background: var(--bn-sunken);
	color: var(--bn-ink);
}

.bn-post-card__menu:focus-visible {
	outline: 2px solid var(--bn-accent);
	outline-offset: 2px;
}

.bn-post-card__menu svg {
	width: 16px;
	height: 16px;
}

.bn-post-card__options-menu {
	position: absolute;
	inset-block-start: calc(100% + 4px);
	inset-inline-end: 0;
	background: var(--bn-surface);
	border: 1px solid var(--bn-line);
	border-radius: var(--bn-r-md);
	box-shadow: var(--bn-shadow-md);
	min-width: 160px;
	z-index: 100;
	overflow: hidden;
}

.bn-post-card__options-menu[hidden] { display: none; }

.bn-post-card__menu-item {
	display: flex;
	align-items: center;
	gap: var(--bn-s2);
	width: 100%;
	padding: var(--bn-s2) var(--bn-s3);
	background: transparent;
	border: 0;
	font-size: var(--bn-text-sm);
	font-family: var(--bn-font-ui);
	color: var(--bn-ink);
	cursor: pointer;
	text-align: start;
	transition: background var(--bn-dur-fast, 120ms) var(--bn-ease, ease);
}

.bn-post-card__menu-item:hover,
.bn-post-card__menu-item:focus { background: var(--bn-sunken); color: var(--bn-ink); }
.bn-post-card__menu-item:focus-visible { outline: 2px solid transparent; box-shadow: inset 0 0 0 2px var(--bn-accent); }
.bn-post-card__menu-item--danger { color: var(--bn-danger); }
.bn-post-card__menu-item--danger:hover,
.bn-post-card__menu-item--danger:focus { background: var(--bn-danger-bg); color: var(--bn-danger); }

/* Already-reported status — a non-interactive confirmation, not an action. */
.bn-post-card__menu-item--reported { color: var(--bn-ink-3); cursor: default; }
.bn-post-card__menu-item--reported:hover,
.bn-post-card__menu-item--reported:focus { background: transparent; color: var(--bn-ink-3); }

/* Content warning overlay — replaces the body until the user clicks "Show anyway". */
.bn-post-card__cw-overlay {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--bn-s2);
	border-radius: var(--bn-r-md);
	background: var(--bn-sunken);
	border: 1px solid var(--bn-line);
	text-align: center;
	padding: var(--bn-s6) var(--bn-s4);
}

.bn-post-card__cw-overlay[hidden] { display: none; }

.bn-post-card__cw-icon {
	display: inline-flex;
	color: var(--bn-warn);
}

.bn-post-card__cw-icon svg {
	width: 24px;
	height: 24px;
}

.bn-post-card__cw-label {
	font-size: var(--bn-text-sm);
	font-weight: 600;
	color: var(--bn-ink-2);
	margin: 0;
}

.bn-post-card__cw-reveal {
	background: var(--bn-surface);
	border: 1px solid var(--bn-line);
	border-radius: var(--bn-r-full);
	padding: 6px var(--bn-s4);
	font-size: var(--bn-text-sm);
	font-weight: 600;
	font-family: var(--bn-font-ui);
	color: var(--bn-ink);
	cursor: pointer;
	transition: border-color var(--bn-dur-fast, 120ms) var(--bn-ease, ease), background var(--bn-dur-fast, 120ms) var(--bn-ease, ease);
}

.bn-post-card__cw-reveal:hover {
	border-color: var(--bn-accent);
	background: var(--bn-accent-100);
	color: var(--bn-accent-700);
}

/* Body block — v2 baseline. JS swaps `bn-post-card__body--blurred` when CW is active. */
.bn-post-card__body {
	display: flex;
	flex-direction: column;
	gap: var(--bn-s3);
	font-size: var(--bn-text-base);
	line-height: 1.6;
	color: var(--bn-ink);
}

.bn-post-card__body--blurred {
	filter: blur(8px);
	pointer-events: none;
	user-select: none;
}

.bn-post-card__content {
	font-size: var(--bn-text-base);
	line-height: 1.6;
	color: var(--bn-ink);
	word-break: break-word;
}

.bn-post-card__content a {
	color: var(--bn-accent);
	text-decoration: none;
}

.bn-post-card__content a:hover { text-decoration: underline; }

/* Hashtag chips — inline accent-tinted pills inside post content. The
   chip background separates tags visually from surrounding text, makes
   them scannable on dense explore cards, and gives the hover a discrete
   target. Mentions stay accent-coloured but un-chipped so the @-prefix
   does the visual lifting. */
.bn-hashtag {
	display: inline-block;
	padding: 0 var(--bn-s2);
	border-radius: var(--bn-r-full, 999px);
	background: var(--bn-accent-bg, color-mix(in oklab, var(--bn-accent) 12%, transparent));
	color: var(--bn-accent);
	font-weight: 500;
	font-size: inherit;
	line-height: 1.5;
	text-decoration: none;
	transition: background var(--bn-dur-fast, 120ms) var(--bn-ease, ease);
}
.bn-hashtag:hover {
	background: var(--bn-accent-bg-strong, color-mix(in oklab, var(--bn-accent) 20%, transparent));
	text-decoration: none;
}

.bn-mention {
	color: var(--bn-accent);
	font-weight: 500;
	text-decoration: none;
}
.bn-mention:hover { text-decoration: underline; }

.bn-post-card__content--announcement {
	font-weight: 500;
}

/* Photo / video / audio media grid + tiles + lightbox live in bn-media.css
 * (BuddyNext\Media\MediaRenderer markup). The legacy media-item grid that
 * used to live here was superseded by the bn-media-tile system and removed
 * to avoid two conflicting .bn-post-card__media-grid definitions. */

/* File attachments. */
.bn-post-card__file-list {
	display: flex;
	flex-direction: column;
	gap: var(--bn-s1);
}

.bn-post-card__file-item {
	display: flex;
	align-items: center;
	gap: var(--bn-s2);
	padding: var(--bn-s2) var(--bn-s3);
	background: var(--bn-sunken);
	border: 1px solid var(--bn-line);
	border-radius: var(--bn-r-md);
	font-size: var(--bn-text-sm);
	color: var(--bn-ink-2);
	cursor: pointer;
	transition: background var(--bn-dur-fast, 120ms) var(--bn-ease, ease);
}

.bn-post-card__file-item:hover { background: var(--bn-line-faint); }

/* Link embed (v2 .embed). */
.bn-post-card__embed,
.bn-post-card__link-preview {
	display: flex;
	gap: var(--bn-s3);
	padding: var(--bn-s3) var(--bn-s4);
	border: 1px solid var(--bn-line);
	border-radius: var(--bn-r-md);
	background: var(--bn-sunken);
	text-decoration: none;
	transition: border-color var(--bn-dur-fast, 120ms) var(--bn-ease, ease);
}

.bn-post-card__link-preview:hover {
	border-color: var(--bn-accent);
}

.bn-post-card__link-thumb {
	width: 64px;
	height: 64px;
	flex-shrink: 0;
	overflow: hidden;
	border-radius: var(--bn-r-sm);
	background: var(--bn-line-faint);
}

.bn-post-card__link-thumb img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.bn-post-card__link-info {
	flex: 1;
	min-width: 0;
}

.bn-post-card__link-title {
	font-size: var(--bn-text-sm);
	font-weight: 600;
	color: var(--bn-ink);
	margin: 0 0 2px;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.bn-post-card__link-desc {
	font-size: var(--bn-text-xs);
	color: var(--bn-ink-3);
	margin: 0 0 var(--bn-s1);
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 2;
	overflow: hidden;
}

.bn-post-card__link-domain {
	display: inline-flex;
	align-items: center;
	gap: var(--bn-s1);
	font-family: var(--bn-font-mono);
	font-size: 10px;
	color: var(--bn-ink-3);
}

/* Poll. */
.bn-post-card__poll-question {
	font-weight: 600;
	margin-block-end: var(--bn-s2);
}

.bn-post-card__poll {
	display: flex;
	flex-direction: column;
	gap: var(--bn-s2);
}

.bn-post-card__poll-option {
	position: relative;
	width: 100%;
	background: transparent;
	border: 1.5px solid var(--bn-line);
	border-radius: var(--bn-r-md);
	padding: var(--bn-s2) var(--bn-s3);
	font-size: var(--bn-text-sm);
	font-family: var(--bn-font-ui);
	color: var(--bn-ink);
	cursor: pointer;
	text-align: start;
	display: flex;
	align-items: center;
	overflow: hidden;
	transition: border-color var(--bn-dur-fast, 120ms) var(--bn-ease, ease);
	min-height: 40px;
}

.bn-post-card__poll-option:hover { border-color: var(--bn-accent); }
.bn-post-card__poll-option.is-voted {
	border-color: var(--bn-accent);
	color: var(--bn-accent-700);
	font-weight: 600;
}
.bn-post-card__poll-option.is-voted .bn-post-card__poll-fill {
	background: var(--bn-accent-100);
}

.bn-post-card__poll-option:focus-visible {
	outline: 2px solid var(--bn-accent);
	outline-offset: 2px;
}

.bn-post-card__poll-fill {
	position: absolute;
	inset-block: 0;
	inset-inline-start: 0;
	background: var(--bn-accent-100);
	transition: width 0.4s ease;
	z-index: 0;
}

.bn-post-card__poll-option-text {
	position: relative;
	z-index: 1;
	flex: 1;
}

.bn-post-card__poll-pct {
	position: relative;
	z-index: 1;
	font-size: var(--bn-text-xs);
	font-weight: 700;
	color: var(--bn-accent-700);
	margin-inline-start: auto;
	padding-inline-start: var(--bn-s2);
}

.bn-post-card__poll-total {
	font-size: var(--bn-text-xs);
	color: var(--bn-ink-3);
	margin: 0;
	padding-block-start: var(--bn-s1);
}

/* Bridge cards: media, discussion, job. */
.bn-post-card__bridge-card {
	display: flex;
	gap: var(--bn-s3);
	align-items: flex-start;
	border: 1px solid var(--bn-line);
	border-radius: var(--bn-r-md);
	padding: var(--bn-s3);
	background: var(--bn-sunken);
}

.bn-post-card__bridge-card--job {
	border-inline-start: 3px solid var(--bn-accent);
}

.bn-post-card__bridge-icon {
	display: inline-flex;
	flex-shrink: 0;
	color: var(--bn-ink-3);
}

/* Event activity card — date pill + title + date/time + location. Mirrors the
   v2 "This week" event rows, sized up for the in-feed body. */
.bn-post-card__event {
	display: flex;
	gap: var(--bn-s3);
	align-items: center;
	border: 1px solid var(--bn-line);
	border-inline-start: 3px solid var(--bn-events);
	border-radius: var(--bn-r-md);
	padding: var(--bn-s3);
	background: var(--bn-events-bg);
}

.bn-post-card__event-date {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	width: 48px;
	flex-shrink: 0;
	text-align: center;
}

.bn-post-card__event-month {
	font-size: var(--bn-text-2xs);
	font-weight: var(--bn-fw-semibold);
	color: var(--bn-events);
	text-transform: uppercase;
	letter-spacing: 0.04em;
}

.bn-post-card__event-day {
	font-family: var(--bn-font-display);
	font-size: var(--bn-text-2xl);
	font-weight: var(--bn-fw-semibold);
	line-height: 1;
	color: var(--bn-ink);
}

.bn-post-card__event-date--tbd .bn-post-card__event-icon {
	display: inline-flex;
	color: var(--bn-events);
}

.bn-post-card__event-info {
	flex: 1;
	min-width: 0;
}

.bn-post-card__event-title {
	font-size: var(--bn-text-base);
	font-weight: var(--bn-fw-semibold);
	line-height: 1.3;
	color: var(--bn-ink);
}

.bn-post-card__event-meta {
	display: flex;
	align-items: center;
	gap: var(--bn-s1);
	font-size: var(--bn-text-xs);
	color: var(--bn-ink-3);
	margin-top: 2px;
}

.bn-post-card__event-meta-icon {
	display: inline-flex;
	flex-shrink: 0;
}

.bn-post-card__event-meta-icon svg {
	width: 13px;
	height: 13px;
}

.bn-post-card__bridge-icon svg {
	width: 20px;
	height: 20px;
}

.bn-post-card__bridge-source {
	font-size: var(--bn-text-xs);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--bn-ink-3);
	display: block;
	margin-block-end: 3px;
}

.bn-post-card__bridge-title {
	display: block;
	font-size: var(--bn-text-base);
	font-weight: 600;
	color: var(--bn-ink);
	line-height: 1.35;
	text-decoration: none;
}
a.bn-post-card__bridge-title:hover { color: var(--bn-accent); }

.bn-post-card__bridge-text {
	font-size: var(--bn-text-sm);
	color: var(--bn-ink-2);
	margin: var(--bn-s1) 0 0;
	line-height: 1.5;
}

/* Reaction summary chips — sits above the action row. */
.bn-post-card__reactions,
.bn-post-card__reaction-summary {
	display: flex;
	gap: var(--bn-s1);
	flex-wrap: wrap;
}

.bn-post-card__summary-chip {
	display: inline-flex;
	align-items: center;
	gap: var(--bn-s1);
	height: 26px;
	padding: 0 var(--bn-s2);
	border-radius: var(--bn-r-full);
	background: var(--bn-sunken);
	font-size: var(--bn-text-xs);
	font-weight: 600;
	color: var(--bn-ink-2);
	border: 1px solid transparent;
	transition: background-color var(--bn-dur-fast, 120ms) var(--bn-ease, ease), border-color var(--bn-dur-fast, 120ms) var(--bn-ease, ease);
}

.bn-post-card__summary-chip svg {
	width: 14px;
	height: 14px;
}

/* Action row (v2 .post-actions): flat, gap, hover bg. */
.bn-post-card__actions {
	display: flex;
	gap: var(--bn-s1);
	padding-block-start: var(--bn-s3);
	border-block-start: 1px solid var(--bn-line-faint);
	align-items: center;
	flex-wrap: nowrap; /* one row always — never break actions onto two rows. */
}

.bn-post-card__action-btn {
	/* Compact, content-width pill actions — sit together at the start of the
	   row instead of stretching to equal full-width columns (which left large
	   dead gaps between the icon+label). */
	flex: 0 0 auto;
	min-width: 0;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	padding: 6px 14px;
	font-size: var(--bn-text-sm);
	font-family: var(--bn-font-ui);
	color: var(--bn-ink-3);
	cursor: pointer;
	background: transparent;
	border: 0;
	border-radius: var(--bn-r-full);
	white-space: nowrap;
	transition: color var(--bn-dur-fast, 120ms) var(--bn-ease, ease), background var(--bn-dur-fast, 120ms) var(--bn-ease, ease);
}

/* Narrow cards (explore grid, mobile) — drop the text label to icon + count so
   all actions stay on one row. The comment count is its own span, so it
   survives; reaction/share counts live in the summary row above. */
@media ( max-width: 480px ) {
	.bn-post-card__action-label { display: none; }
}

.bn-post-card__action-btn svg,
.bn-post-card__action-btn img {
	width: 16px;
	height: 16px;
	stroke-width: 1.75;
}

.bn-post-card__action-btn:hover,
.bn-post-card__action-btn:focus {
	color: var(--bn-accent);
	background: var(--bn-sunken);
	/* Geometry (padding/border) is locked once in bn-base.css under .bn-app
	   (theme-proof interactive controls) — base == hover, so no per-state lock. */
}

.bn-post-card__action-btn:focus-visible {
	outline: 2px solid var(--bn-accent);
	outline-offset: 2px;
}

.bn-post-card__action-btn.is-reacted { color: var(--bn-danger); }
.bn-post-card__action-btn.is-bookmarked { color: var(--bn-warn); }
.bn-post-card__action-btn.is-shared { color: var(--bn-accent); }

/* Count beside the Comment / Share icon. Distinct from __action-label, so it
   stays visible in the icon-only narrow layout (explore grid / mobile). */
.bn-post-card__action-count { font-size: var(--bn-text-sm); font-weight: 600; color: var(--bn-ink-2); }
.bn-post-card__action-count[hidden] { display: none; }

.bn-post-card__action-label {
	font-size: var(--bn-text-sm);
	font-weight: 500;
	color: inherit;
}

/* Share label cycles between "Share" / "Share · N" / "Shared" / "Shared · N"
   (~38px → 75px range). Reserve the worst-case width so clicking Share
   doesn't reflow the entire action row. */
.bn-post-card__action-label[data-wp-text="state.shareLabel"] {
	display: inline-block;
	min-inline-size: 70px;
	text-align: start;
}

/* Reaction picker wrapper + dropdown panel. */
.bn-post-card__react-wrap { position: relative; }

.bn-post-card__emoji-picker {
	position: absolute;
	inset-block-end: calc(100% + 8px);
	inset-inline-start: 50%;
	transform: translateX(-50%);
	display: flex;
	gap: var(--bn-s1);
	background: var(--bn-surface);
	border: 1px solid var(--bn-line);
	border-radius: var(--bn-r-full);
	padding: 6px 10px;
	box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);
	z-index: 1000;
	white-space: nowrap;
}

/* Flip downward when the React trigger sits near the top of the viewport, so
   the upward-opening picker never paints over the sticky header (the JS adds
   this modifier on open via collision detection). */
.bn-post-card__emoji-picker--below {
	inset-block-end: auto;
	inset-block-start: calc(100% + 8px);
}

.bn-post-card__emoji-picker[hidden] { display: none; }

/* Each picker button — Facebook-style: hover lifts and scales the
   emoji 1.4× with a 200ms cubic-bezier bounce; the neighbours subtly
   compress. Click triggers a quick pop animation before the picker
   closes. */
.bn-post-card__emoji-btn {
	background: transparent;
	border: 0;
	cursor: pointer;
	line-height: 1;
	padding: var(--bn-s1);
	border-radius: 50%;
	transition: transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
	transform-origin: center bottom;
}
.bn-post-card__emoji-btn:hover {
	transform: scale(1.4) translateY(-4px);
}
.bn-post-card__emoji-btn:active {
	animation: bn-react-pop 240ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.bn-post-card__emoji-btn:focus-visible {
	outline: 2px solid var(--bn-accent);
	outline-offset: 2px;
}
@keyframes bn-react-pop {
	0%   { transform: scale(1); }
	50%  { transform: scale(1.6) translateY(-6px); }
	100% { transform: scale(1.4) translateY(-4px); }
}

/* Emoji image inside picker buttons. */
.bn-reaction-icon {
	display: inline-flex;
	width: 28px;
	height: 28px;
	/* Never let the flex picker compress the emoji to 0. */
	flex: 0 0 auto;
}
.bn-reaction-icon img,
.bn-reaction-icon svg {
	width: 28px;
	height: 28px;
	display: block;
	/* Defend against host themes whose global `img { max-width: 100% }`
	   collapses the emoji inside the flex picker — BN owns its own sizing. */
	max-width: none;
	flex-shrink: 0;
}

/* Letter-glyph fallback for custom (Pro) reactions that ship no SVG — the
   reaction's accent colour is set inline on .bn-reaction-icon. Render it as a
   filled circular badge that fills the 28px slot so it sits cleanly among the
   SVG emoji instead of as misaligned bare text. */
.bn-reaction-glyph {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	height: 100%;
	border-radius: 50%;
	background: color-mix(in oklab, currentColor 18%, transparent);
	color: inherit;
	font-size: 0.8125rem;
	font-weight: 700;
	line-height: 1;
}

/* React trigger button — shows a Lucide heart outline when idle. Once
   the user has reacted, CSS replaces the icon with the colored Fluent
   Emoji matching their reaction via background-image + masking the
   underlying SVG. The colored emoji and the trigger label give the
   Facebook-style "you reacted with X" affordance. */
.bn-post-card__react-icon {
	display: inline-flex;
	width: 18px;
	height: 18px;
	align-items: center;
	justify-content: center;
	transition: transform 180ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.bn-post-card__react-icon svg { width: 18px; height: 18px; }

/* Reacted states — hide the Lucide SVG and paint the colored emoji as
   a background image. The Fluent Emoji vendor lives at
   /assets/emoji/{slug}.svg (registered via IconService::emoji_url). */
.bn-post-card__react-icon--like,
.bn-post-card__react-icon--love,
.bn-post-card__react-icon--haha,
.bn-post-card__react-icon--wow,
.bn-post-card__react-icon--sad,
.bn-post-card__react-icon--angry {
	background-position: center;
	background-repeat: no-repeat;
	background-size: contain;
	transform: scale(1.05);
}
.bn-post-card__react-icon--like  svg,
.bn-post-card__react-icon--love  svg,
.bn-post-card__react-icon--haha  svg,
.bn-post-card__react-icon--wow   svg,
.bn-post-card__react-icon--sad   svg,
.bn-post-card__react-icon--angry svg {
	display: none;
}
.bn-post-card__react-icon--like  { background-image: url("../emoji/like.svg"); }
.bn-post-card__react-icon--love  { background-image: url("../emoji/love.svg"); }
.bn-post-card__react-icon--haha  { background-image: url("../emoji/haha.svg"); }
.bn-post-card__react-icon--wow   { background-image: url("../emoji/wow.svg"); }
.bn-post-card__react-icon--sad   { background-image: url("../emoji/sad.svg"); }
.bn-post-card__react-icon--angry { background-image: url("../emoji/angry.svg"); }

/* React button label colour-matches the reacted state for an extra
   FB-style affordance ("Like" turns blue when you've liked, etc.). */
.bn-post-card__action-btn--react.is-reacted .bn-post-card__action-label { color: var(--bn-accent); font-weight: 600; }

/* ── Feed tabs ──────────────────────────────────────────────────────────────
   Feed tabs use the .bn-tabs + .bn-tab primitives from bn-base.css. The
   inner feed-stack layout (.bn-feed-stack) owns the vertical spacing
   between the composer, tabs, announcement, and post list — no margin
   needed here. */
.bn-feed-tabs {
	margin: 0;
}

/* ── Sidebar row helpers ────────────────────────────────────────────────
   The shell's right column auto-wraps everything in .bn-app__right; this
   block adds widget-local affordances missing from the bn-sbar-row base
   primitive in bn-base.css (gap, info slot, link variant). */
.bn-sbar-row {
	gap: var(--bn-s3);
	justify-content: space-between;
}
.bn-sbar-row__info {
	flex: 1;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: 2px;
}
.bn-sbar-row__info .bn-sbar-row__name { display: block; }
.bn-sbar-row__info .bn-sbar-row__meta { display: block; }
.bn-sbar-row--link {
	text-decoration: none;
	color: inherit;
}
.bn-sbar-row--link:hover .bn-sbar-row__name { color: var(--bn-brand); }

/* ── Empty state ────────────────────────────────────────────────────────────── */
.bn-feed-empty {
	text-align: center;
	padding: var(--bn-s10) var(--bn-s6);
	background: var(--bn-surface);
	border: 1px solid var(--bn-border);
	border-radius: var(--bn-r-xl);
	color: var(--bn-text-3);
}

.bn-feed-empty__icon { font-size: var(--text-4xl); margin-bottom: var(--bn-s3); }

.bn-feed-empty__title {
	font-family: var(--bn-font-display);
	font-size: var(--bn-text-lg);
	font-weight: var(--fw-bold);
	color: var(--bn-text-2);
	margin: 0 0 var(--bn-s2);
}

.bn-feed-empty__text {
	font-size: var(--bn-text-sm);
	line-height: var(--leading-body);
	margin: 0 0 var(--bn-s4);
}

/* .bn-feed-empty__cta uses the .bn-btn[data-variant="primary"] primitive. */


/* ── Skeleton loader ────────────────────────────────────────────────────────── */
@keyframes bn-shimmer {
	0%   { background-position: -600px 0; }
	100% { background-position: 600px 0; }
}

.bn-skeleton {
	background: var(--bn-surface);
	border: 1px solid var(--bn-border);
	border-radius: var(--bn-r-xl);
	padding: var(--bn-s4);
	margin-bottom: var(--bn-s3);
}

.bn-skeleton-line {
	height: 14px;
	border-radius: var(--bn-r-sm);
	background: linear-gradient(
		90deg,
		var(--bn-bg-subtle) 25%,
		var(--bn-bg-hover)  50%,
		var(--bn-bg-subtle) 75%
	);
	background-size: 1200px 100%;
	animation: bn-shimmer 1.4s ease-in-out infinite;
	margin-bottom: var(--bn-s2);
}

.bn-skeleton-avatar {
	width: 40px;
	height: 40px;
	border-radius: 50%;
	background: linear-gradient(
		90deg,
		var(--bn-bg-subtle) 25%,
		var(--bn-bg-hover)  50%,
		var(--bn-bg-subtle) 75%
	);
	background-size: 1200px 100%;
	animation: bn-shimmer 1.4s ease-in-out infinite;
}

.bn-skeleton-header {
	display: flex;
	gap: var(--bn-s3);
	align-items: center;
	margin-bottom: var(--bn-s3);
}

.bn-skeleton-meta { flex: 1; }

.bn-skeleton-line--short { width: 40%; }
.bn-skeleton-line--medium { width: 70%; }
.bn-skeleton-line--full { width: 100%; }
.bn-skeleton-line--small { height: 10px; }

/* ── Pagination ─────────────────────────────────────────────────────────────── */
.bn-feed-pagination {
	display: flex;
	justify-content: center;
	gap: var(--bn-s2);
	margin-top: var(--bn-s5);
	flex-wrap: wrap;
}

.bn-feed-page-btn {
	padding: var(--bn-s2) var(--bn-s3);
	border: 1.5px solid var(--bn-border);
	border-radius: var(--bn-r-sm);
	font-size: var(--bn-text-sm);
	font-weight: var(--fw-medium);
	color: var(--bn-text-2);
	background: var(--bn-surface);
	text-decoration: none;
	transition: border-color var(--bn-transition), color var(--bn-transition), background var(--bn-transition);
}

.bn-feed-page-btn:hover {
	border-color: var(--bn-brand);
	color: var(--bn-brand);
}

.bn-feed-page-btn.current {
	background: var(--bn-brand);
	border-color: var(--bn-brand);
	color: var(--text-on-brand);
	font-weight: var(--fw-bold);
}

.bn-feed-page-btn[disabled] {
	opacity: 0.4;
	pointer-events: none;
}

/* ── Shared avatar utility ──────────────────────────────────────────────────── */
.bn-avatar {
	border-radius: 50%;
	color: var(--text-on-brand);
	font-weight: var(--fw-bold);
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	letter-spacing: var(--ls-wide);
	overflow: hidden;
}

.bn-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
.bn-avatar.sm { width: 34px; height: 34px; font-size: var(--bn-text-xs); }
.bn-avatar.xs { width: 26px; height: 26px; font-size: var(--text-xs); }

/* ── Dark mode overrides ────────────────────────────────────────────────────── */
[data-theme="dark"] .bn-post-card                { background: var(--bn-surface); border-color: var(--bn-border); }
[data-theme="dark"] .bn-composer                 { background: var(--bn-surface); border-color: var(--bn-border); }
[data-theme="dark"] .bn-feed-tabs                { background: var(--bn-surface); border-color: var(--bn-border); }
[data-theme="dark"] .bn-feed-tab                 { background: var(--bn-surface); border-color: var(--bn-border); }
[data-theme="dark"] .bn-post-card__summary-chip  { background: var(--bn-bg-hover); border-color: var(--bn-border); }
[data-theme="dark"] .bn-post-card__options-menu  { background: var(--bn-surface); border-color: var(--bn-border); }
[data-theme="dark"] .bn-post-card__link-preview  { background: var(--bn-bg-hover); border-color: var(--bn-border); }
[data-theme="dark"] .bn-post-card__bridge-card   { background: var(--bn-bg-hover); border-color: var(--bn-border); }
[data-theme="dark"] .bn-post-card__cw-overlay    { background: var(--bn-bg-hover); border-color: var(--bn-border); }
[data-theme="dark"] .bn-post-card__poll-option   { border-color: var(--bn-border); color: var(--bn-text-1); }
[data-theme="dark"] .bn-post-card__poll-fill     { background: var(--bn-brand-light); }
[data-theme="dark"] .bn-post-card__emoji-picker  { background: var(--bn-surface); border-color: var(--bn-border); }
[data-theme="dark"] .bn-skeleton                 { background: var(--bn-surface); border-color: var(--bn-border); }
[data-theme="dark"] .bn-feed-empty               { background: var(--bn-surface); border-color: var(--bn-border); }
[data-theme="dark"] .bn-post-card__announcement-bar { background: var(--bn-brand-light); }

/* ── Responsive — mobile (≤640px) ──────────────────────────────────────────────
   The shell (bn-shell.css) owns the rail + right-sidebar collapse breakpoints;
   inner-template overrides here only tweak content rhythm at small widths. */
@media (max-width: 640px) {
	.bn-feed-stack { gap: var(--bn-s3); }

	.bn-post-card__head { flex-wrap: wrap; }
	/* Hide the inline per-post Follow on mobile. Repeated on every card it eats a
	   full row each and clutters a dense feed (Facebook doesn't surface Follow on
	   every feed post); the action stays available via the post's overflow (···)
	   menu and the author's profile. Desktop has the width, so it keeps the
	   inline Follow pill (see .bn-post-card__follow base rule). */
	.bn-post-card__follow { display: none; }

	/* Facebook-style compact byline on narrow cards: author name on row 1, then
	   ONE meta line — "Space · time · audience-icon" — on row 2 (never a 3rd
	   row). The @handle (Facebook has no handles; it just repeats the name above)
	   and the privacy WORD (the lock icon + aria-label convey it) are dropped on
	   mobile only — desktop is unchanged. The meta stays on one line: if the
	   space name is long it truncates with an ellipsis while the dot separators,
	   time, and privacy icon stay fully pinned. */
	.bn-post-card__meta .bn-post-card__handle,
	.bn-post-card__meta .bn-post-card__handle + .bn-post-card__sep {
		display: none;
	}
	.bn-post-card__privacy > span { display: none; }
	.bn-post-card__meta { flex-wrap: nowrap; min-width: 0; gap: 3px; }
	.bn-post-card__meta > * { flex-shrink: 0; }
	.bn-post-card__space-link { min-width: 0; flex-shrink: 1; }
	.bn-post-card__space-link .bn-icon { flex-shrink: 0; }
	.bn-post-card__space-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

	.bn-composer { padding: var(--bn-s3); }

	.bn-post-card {
		padding: var(--bn-s4);
		gap: var(--bn-s3);
	}

	.bn-post-card__actions {
		gap: var(--bn-s3);
	}

	.bn-post-card__action-btn {
		font-size: var(--bn-text-xs);
		gap: var(--bn-s1);
		padding: 6px;
	}

	.bn-post-card__action-label {
		font-size: var(--bn-text-xs);
	}

	.bn-post-card__link-thumb {
		width: 56px;
		height: 56px;
	}

	.bn-post-card__emoji-picker {
		inset-inline-start: 0;
		inset-inline-end: auto;
		transform: none;
	}

	.bn-feed-tab {
		font-size: var(--bn-text-xs);
		padding: var(--bn-s2);
	}

	.bn-post-card__options-menu {
		inset-inline-end: 0;
		min-width: 140px;
	}
}

/* ── Comments expand region ────────────────────────────────────────────────── */

.bn-post-card__comments {
	border-block-start: 1px solid var(--bn-line-faint);
	padding-block-start: var(--bn-s3);
	display: flex;
	flex-direction: column;
	gap: var(--bn-s3);
}

.bn-comment-list {
	display: flex;
	flex-direction: column;
	gap: var(--bn-s3);
}

.bn-comment {
	display: flex;
	gap: var(--bn-s3);
}

.bn-comment--reply {
	margin-inline-start: var(--bn-s8);
}

.bn-comment__avatar {
	width: 32px;
	height: 32px;
	border-radius: var(--bn-r-full);
	background: var(--bn-accent);
	color: var(--bn-surface);
	font-size: var(--bn-text-xs);
	font-weight: 600;
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
}

.bn-comment__body {
	flex: 1;
	min-width: 0;
}

.bn-comment__header {
	display: flex;
	align-items: baseline;
	gap: var(--bn-s2);
	margin-block-end: var(--bn-s1);
}

.bn-comment__author {
	font-size: var(--bn-text-sm);
	font-weight: 600;
	color: var(--bn-ink);
}

.bn-comment__time {
	font-size: var(--bn-text-xs);
	color: var(--bn-ink-3);
}

.bn-comment__content {
	font-size: var(--bn-text-sm);
	color: var(--bn-ink);
	line-height: 1.6;
	margin: 0 0 var(--bn-s1);
	white-space: pre-wrap;
	word-break: break-word;
}

.bn-comment__actions {
	display: flex;
	gap: var(--bn-s3);
}

.bn-comment__reply-btn,
.bn-comment__delete-btn,
.bn-comment__reply-cancel {
	background: transparent;
	border: 0;
	padding: 0;
	font-size: var(--bn-text-xs);
	color: var(--bn-ink-2);
	cursor: pointer;
	font-weight: 500;
}

.bn-comment__reply-btn:hover { color: var(--bn-accent); }
.bn-comment__delete-btn:hover { color: var(--bn-danger); }
.bn-comment__reply-cancel:hover { color: var(--bn-ink); }

.bn-comment__reply-form {
	display: flex;
	align-items: center;
	gap: var(--bn-s2);
	margin-block-start: var(--bn-s2);
}

/* Per-selector [hidden] guard so a collapsed reply form is actually hidden,
   rather than relying solely on the global [hidden]{display:none!important}
   reset (which a theme/specificity override could defeat). The display:flex
   above only applies to the visible state. */
.bn-comment__reply-form[hidden] {
	display: none;
}

.bn-comment__replies {
	display: flex;
	flex-direction: column;
	gap: var(--bn-s3);
	margin-block-start: var(--bn-s3);
}

.bn-comment-form {
	display: flex;
	align-items: flex-end;
	gap: var(--bn-s2);
}

.bn-comment-form__avatar { flex-shrink: 0; }

.bn-comment-form__input,
.bn-comment__reply-input {
	flex: 1;
	min-width: 0;
	border: 1px solid var(--bn-line);
	border-radius: var(--bn-r-md);
	padding: var(--bn-s2) var(--bn-s3);
	font-size: var(--bn-text-sm);
	font-family: var(--bn-font-ui);
	color: var(--bn-ink);
	background: var(--bn-sunken);
	resize: none;
	line-height: 1.5;
	transition: border-color var(--bn-dur-fast, 120ms) var(--bn-ease, ease), background var(--bn-dur-fast, 120ms) var(--bn-ease, ease);
}

.bn-comment-form__input:focus,
.bn-comment__reply-input:focus {
	outline: 0;
	border-color: var(--bn-accent);
	background: var(--bn-surface);
}

/* Icon-only circular submit — overrides .bn-btn[data-variant="primary"] sizing. */
.bn-comment-form__submit.bn-btn,
.bn-comment-form__submit {
	width: 40px;
	height: 40px;
	min-height: 40px;
	padding: 0;
	border-radius: var(--bn-r-full);
	flex-shrink: 0;
}

.bn-comment-form__submit svg {
	width: 16px;
	height: 16px;
	stroke: currentColor;
	fill: none;
	stroke-width: 2;
}

.bn-comment-count {
	display: inline-block;
	min-inline-size: 22px;
	font-size: var(--bn-text-xs);
	font-weight: 600;
	color: inherit;
	text-align: end;
}

/* Reserve space when the count is 0 so the badge appearing on first
   comment doesn't grow the button (~14-22px CLS otherwise). The
   literal `hidden` attribute is enforced by a global
   `[hidden] { display: none !important; }` reset, so the override
   here must also be !important to keep the badge in flow. */
.bn-comment-count[hidden] {
	display: inline-block !important;
	visibility: hidden;
}

/* Comments mobile */
@media (max-width: 640px) {
	.bn-comment--reply { margin-inline-start: var(--bn-s4); }
}

/* ── Threaded comments (wave 2) ────────────────────────────────────────────
 * .bn-comment-card is the wave-2 node built by buildCommentNode in
 * store.js. It supersedes .bn-comment for new code paths but inherits
 * most styles. Depth is exposed via data-depth so indentation can be
 * computed without spawning N classes.
 */
.bn-comment-card {
	display: flex;
	gap: var(--bn-s3);
}

.bn-comment-card[data-depth="1"] {
	margin-inline-start: var(--bn-comment-indent, 32px);
}

.bn-comment-card[data-depth="2"] {
	margin-inline-start: calc(var(--bn-comment-indent, 32px) * 2);
}

.bn-comment-card--pinned {
	background: var(--bn-sunken);
	border-radius: var(--bn-r-md);
	padding: var(--bn-s2);
}

.bn-comment-card--deleted .bn-comment__content {
	color: var(--bn-ink-3);
	font-style: italic;
}

.bn-comment__pinned-badge {
	font-size: var(--bn-text-xs);
	font-weight: 600;
	color: var(--bn-accent);
	background: var(--bn-accent-soft, rgba(0, 0, 0, 0.05));
	padding: 0 var(--bn-s2);
	border-radius: var(--bn-r-sm);
}

.bn-comment__edited {
	font-size: var(--bn-text-xs);
	color: var(--bn-ink-3);
	font-style: italic;
}

.bn-comment__like-btn,
.bn-comment__edit-btn,
.bn-comment__pin-btn,
.bn-comment__report-btn {
	background: transparent;
	border: 0;
	padding: 0;
	font-size: var(--bn-text-xs);
	color: var(--bn-ink-2);
	cursor: pointer;
	font-weight: 500;
	min-height: var(--bn-tap, 40px);
	display: inline-flex;
	align-items: center;
	gap: var(--bn-s1);
}

.bn-comment__like-btn:hover { color: var(--bn-danger); }
.bn-comment__edit-btn:hover { color: var(--bn-accent); }
.bn-comment__pin-btn:hover { color: var(--bn-accent); }
.bn-comment__report-btn:hover { color: var(--bn-danger); }

/* Only style the reacted state when data-reaction holds an actual emoji —
   the JS sets data-reaction="" (empty but present) on un-reacted comments,
   which a bare [data-reaction] would wrongly match. */
.bn-comment__like-btn[data-reaction]:not([data-reaction=""]) {
	color: var(--bn-accent);
	font-weight: 600;
}
.bn-comment__like-btn[data-reaction="love"]  { color: var(--bn-danger); }
.bn-comment__like-btn[data-reaction="angry"] { color: var(--bn-danger); }
.bn-comment__like-btn[data-reaction="haha"]  { color: var(--bn-warn); }
.bn-comment__like-btn[data-reaction="wow"]   { color: var(--bn-warn); }
.bn-comment__like-btn[data-reaction="sad"]   { color: var(--bn-accent); }

.bn-comment__like-icon {
	font-size: var(--bn-text-base);
	line-height: 1;
	display: inline-flex;
	align-items: center;
}
.bn-comment__like-icon img {
	width: 16px;
	height: 16px;
	display: block;
}

/* Comment reaction picker — mirrors the post-card picker but smaller
   (24px emoji vs 28px). Hover-reveals on the .bn-comment__react-wrap
   container with the same FB-style bounce on each emoji button. */
.bn-comment__react-wrap {
	position: relative;
	display: inline-flex;
}
.bn-comment__react-picker {
	position: absolute;
	inset-block-end: calc(100% + 6px);
	inset-inline-start: 0;
	display: flex;
	gap: 2px;
	background: var(--bn-surface);
	border: 1px solid var(--bn-line);
	border-radius: var(--bn-r-full);
	padding: var(--bn-s1) var(--bn-s2);
	box-shadow: 0 6px 18px rgba(0, 0, 0, 0.14);
	z-index: 1000;
	white-space: nowrap;
}
.bn-comment__react-picker[hidden] { display: none; }
.bn-comment__react-option {
	background: transparent;
	border: 0;
	cursor: pointer;
	line-height: 0;
	padding: 3px;
	border-radius: 50%;
	transition: transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
	transform-origin: center bottom;
}
.bn-comment__react-option:hover {
	transform: scale(1.4) translateY(-3px);
}
.bn-comment__react-option:focus-visible {
	outline: 2px solid var(--bn-accent);
	outline-offset: 2px;
}
.bn-comment__react-option img {
	width: 24px;
	height: 24px;
	display: block;
	/* Same theme-proofing as the post reaction picker. */
	max-width: none;
	flex-shrink: 0;
}

.bn-comment__edit-form {
	display: flex;
	flex-direction: column;
	gap: var(--bn-s2);
	margin-block-end: var(--bn-s2);
}

.bn-comment__edit-form .bn-comment-form__submit {
	align-self: flex-start;
	width: auto;
	height: auto;
	min-height: var(--bn-tap, 40px);
	border-radius: var(--bn-r-md);
	padding: var(--bn-s1) var(--bn-s3);
}

/* Footer row for the edit form so emoji + Save + Cancel align on one line
   instead of stretching full-width in the column layout. */
.bn-comment__edit-actions {
	display: flex;
	align-items: center;
	gap: var(--bn-s2);
}

.bn-comment__edit-actions .bn-comment-form__submit {
	align-self: center;
}

/* Reply-form submit reuses .bn-comment-form__submit (the circular icon button)
   but carries a text label ("Reply"), which the 40x40 icon sizing clipped and
   left misaligned next to the text Cancel link. Match the edit-form override so
   it renders as a normal text button aligned with Cancel. */
.bn-comment__reply-form .bn-comment-form__submit {
	align-self: center;
	width: auto;
	height: auto;
	min-height: var(--bn-tap, 40px);
	border-radius: var(--bn-r-md);
	padding: var(--bn-s1) var(--bn-s3);
}

/* Inline post editor — opened from the post card kebab → Edit. Pre-filled
   textarea + Save/Cancel, replacing the rendered content in place. */
.bn-post-card__edit-form {
	display: flex;
	flex-direction: column;
	gap: var(--bn-s2);
	margin-block: var(--bn-s2);
}

.bn-post-card__edit-input {
	width: 100%;
	box-sizing: border-box;
	resize: vertical;
	min-height: calc(var(--bn-tap, 40px) * 2);
	padding: var(--bn-s2) var(--bn-s3);
	border: 1px solid var(--bn-line);
	border-radius: var(--bn-r-md);
	background: var(--bn-surface);
	color: var(--bn-ink);
	font: inherit;
	line-height: 1.5;
}

.bn-post-card__edit-input:focus-visible {
	outline: 2px solid var(--bn-accent);
	outline-offset: 1px;
}

.bn-post-card__edit-actions {
	display: flex;
	gap: var(--bn-s2);
	flex-wrap: wrap;
}

.bn-post-card__edited {
	color: var(--bn-text-subtle, var(--bn-muted, inherit));
	font-size: 0.85em;
}

/* Comment loading skeleton (three rows while the thread fetches). */
.bn-comment-skeleton {
	display: flex;
	gap: var(--bn-s3);
	align-items: center;
	padding: var(--bn-s2) 0;
}

.bn-comment-skeleton__avatar {
	width: 32px;
	height: 32px;
	border-radius: var(--bn-r-full);
	flex-shrink: 0;
}

.bn-comment-skeleton__line {
	flex: 1;
	height: 14px;
	border-radius: var(--bn-r-sm);
}

/* Comment thread load failure. */
.bn-comment-error {
	font-size: var(--bn-text-sm);
	color: var(--bn-ink-2);
	padding: var(--bn-s3);
	background: var(--bn-sunken);
	border-radius: var(--bn-r-md);
	display: flex;
	gap: var(--bn-s2);
	align-items: center;
	flex-wrap: wrap;
}

.bn-comment-error__retry {
	background: transparent;
	border: 0;
	padding: 0;
	color: var(--bn-accent);
	cursor: pointer;
	font-weight: 600;
	text-decoration: underline;
	min-height: var(--bn-tap, 40px);
}

/* Inline comment-submit error rail above the textarea. */
.bn-comment-submit-error {
	display: flex;
	gap: var(--bn-s2);
	align-items: center;
	padding: var(--bn-s2) var(--bn-s3);
	background: var(--bn-danger-bg);
	color: var(--bn-danger);
	border-radius: var(--bn-r-md);
	font-size: var(--bn-text-sm);
	margin-block-end: var(--bn-s2);
}

.bn-comment-submit-error__retry {
	background: transparent;
	border: 0;
	padding: 0;
	color: var(--bn-danger);
	cursor: pointer;
	font-weight: 600;
	text-decoration: underline;
	min-height: var(--bn-tap, 40px);
	margin-inline-start: auto;
}

/* Threaded comments — mobile collapse */
@media (max-width: 480px) {
	.bn-comment-card[data-depth="1"],
	.bn-comment-card[data-depth="2"] {
		/* Flatten deeper threads at mobile: only 16px indent at depth 1,
		   no further indent for depth 2 to avoid horizontal scroll. */
		margin-inline-start: 16px;
	}
	.bn-comment__actions {
		flex-wrap: wrap;
		gap: var(--bn-s2);
	}
}

/* ── Shared-post embed (v2 .post-card__shared blockquote) ──────────────────── */
.bn-post-card__shared,
.bn-post-card__shared-embed {
	border: 1px solid var(--bn-line);
	border-radius: var(--bn-r-md);
	padding: var(--bn-s4);
	background: var(--bn-sunken);
	margin: 0;
}

.bn-post-card__shared-header {
	display: flex;
	align-items: center;
	gap: var(--bn-s2);
	margin-block-end: var(--bn-s3);
}

.bn-post-card__shared-avatar-link {
	display: inline-flex;
	flex-shrink: 0;
}

.bn-post-card__shared-meta {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
}

.bn-post-card__shared-name {
	font-size: var(--bn-text-sm);
	font-weight: 600;
	color: var(--bn-ink);
	text-decoration: none;
}

.bn-post-card__shared-name:hover { text-decoration: underline; }

.bn-post-card__shared-sub {
	display: inline-flex;
	align-items: center;
	gap: var(--bn-s1);
	flex-wrap: wrap;
}

.bn-post-card__shared-username,
.bn-post-card__shared-time {
	font-size: var(--bn-text-xs);
	color: var(--bn-ink-3);
}

.bn-post-card__shared-content-link {
	display: block;
	text-decoration: none;
	color: inherit;
}
.bn-post-card__shared-content-link:hover .bn-post-card__shared-content {
	color: var(--brand);
}

.bn-post-card__shared-content {
	display: block;
	font-size: var(--bn-text-sm);
	color: var(--bn-ink);
	line-height: 1.6;
}

.bn-post-card__shared-viewlink {
	display: inline-block;
	margin-top: var(--bn-s2);
	font-size: var(--bn-text-xs);
	font-weight: var(--fw-semibold);
	color: var(--brand);
}
.bn-post-card__shared-content-link:hover .bn-post-card__shared-viewlink {
	text-decoration: underline;
}

/* Media / link-video preview inside a reshare embed. Without this a reshared
   photo or YouTube post showed only "[No text content]" — a blank quote. */
.bn-post-card__shared-thumb {
	position: relative;
	display: block;
	margin-top: var(--bn-s2);
	border-radius: var(--bn-r-md);
	overflow: hidden;
	background: var(--bn-surface-2, var(--bn-elev-1));
	aspect-ratio: 16 / 9;
}
.bn-post-card__shared-thumb img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}
.bn-post-card__shared-play {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	color: #fff;
	background: rgba(0, 0, 0, 0.28);
	pointer-events: none;
}
.bn-post-card__shared-play .bn-icon {
	width: 44px;
	height: 44px;
	padding: 10px;
	border-radius: 50%;
	background: rgba(0, 0, 0, 0.55);
}
.bn-post-card__shared-linktitle {
	display: block;
	margin-top: var(--bn-s2);
	font-size: var(--bn-text-sm);
	font-weight: var(--fw-semibold);
	color: var(--bn-ink);
	line-height: 1.4;
}
.bn-post-card__shared-content-link:hover .bn-post-card__shared-linktitle {
	color: var(--brand);
}

.bn-post-card__shared-empty,
.bn-post-card__shared-missing {
	font-size: var(--bn-text-sm);
	color: var(--bn-ink-2);
	display: flex;
	align-items: center;
	gap: var(--bn-s2);
}

/* ═══════════════════════════════════════════════════════════════════════════════
   COMPOSER (partials/composer.php) — single-state v2 markup
   Matches docs/v2 Plans/v2/home-feed.html `.composer`. Avatar +
   textarea + tools row (icons + spacer + privacy + Share). No collapsed
   pill — always visible.
   ═══════════════════════════════════════════════════════════════════════════ */

.bn-composer {
	position: relative;
	background: var(--bn-surface);
	border: 1px solid var(--border);
	border-radius: var(--bn-r-lg);
	padding: var(--s4) var(--s5) var(--s3);
	display: flex;
	gap: var(--s3);
	align-items: flex-start;
}

.bn-composer__avatar {
	width: 36px;
	height: 36px;
	border-radius: 50%;
	background: var(--brand);
	color: var(--text-on-brand);
	font-size: var(--text-sm);
	font-weight: 700;
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	overflow: hidden;
}
.bn-composer__avatar img { width: 100%; height: 100%; object-fit: cover; }

.bn-composer__input {
	flex: 1;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: var(--s3);
}

.bn-composer__prompt {
	width: 100%;
	border: none;
	background: transparent;
	font-family: var(--font-body);
	font-size: var(--text-base);
	color: var(--text-1);
	resize: none;
	/* Horizontal padding so the cursor/text don't sit flush against the
	   left edge — matches the composer's --s4 visual rhythm. */
	padding: 6px var(--s4);
	line-height: 1.5;
	outline: none;
}
.bn-composer__prompt::placeholder { color: var(--text-3); }
.bn-composer__prompt:focus-visible { outline: none; }

/*
 * Char counter — populated by JS into the toolbar slot
 * (.bn-composer__char-counter-slot) when present; falls back to a
 * block element after the textarea on comment forms.
 */
.bn-composer__char-counter,
.bn-composer__char-counter-slot {
	font-size: var(--text-2xs, 11px);
	color: var(--text-3);
	font-variant-numeric: tabular-nums;
}
.bn-composer__char-counter {
	display: block;
	margin-block-start: 4px;
}
.bn-composer__char-counter-slot { display: inline-flex; align-items: center; }
.bn-composer__char-counter-slot:empty { display: none; }
.bn-composer__char-counter[data-state="near"],
.bn-composer__char-counter-slot[data-state="near"] { color: var(--bn-warn, #b45309); }
.bn-composer__char-counter[data-state="over"],
.bn-composer__char-counter-slot[data-state="over"] { color: var(--bn-danger, #dc2626); font-weight: 600; }

/* Active tool (e.g. Poll button when poll panel is open). */
.bn-composer__tool[aria-pressed="true"] {
	background: var(--bn-accent-100);
	color: var(--bn-accent);
}

/* Drag-drop overlay state — outline + faint accent wash when the user
   is dragging files over the composer. */
.bn-composer--dragover {
	outline: 2px dashed var(--bn-accent);
	outline-offset: -2px;
	background: color-mix(in oklab, var(--bn-accent) 6%, transparent);
}

/* @ / # typeahead dropdown — appears under the textarea, scoped to the
   composer wrap so it doesn't bleed into other modules. */
.bn-composer__typeahead {
	position: absolute;
	z-index: 50;
	margin-top: var(--bn-s1);
	min-width: 220px;
	max-width: 320px;
	background: var(--surface, #fff);
	border: 1px solid var(--border, #e5e7eb);
	border-radius: var(--r-md, 8px);
	box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
	/* Bound the results list so a large match set scrolls inside the dropdown
	   (showing ~7-8 members at once) instead of ballooning the page. */
	max-height: 320px;
	overflow-x: hidden;
	overflow-y: auto;
}
/* The invite-member modal's body is overflow-y:auto, which would clip this
   absolutely-positioned dropdown (it sits below the input). The body content
   is short, so let the dropdown extend past it instead of being cut off. */
[data-bn-modal="invite-member"] .bn-modal__body {
	overflow: visible;
}
.bn-composer__typeahead-item {
	display: flex;
	align-items: center;
	gap: var(--s2, 8px);
	width: 100%;
	padding: var(--bn-s2) var(--bn-s3);
	background: transparent;
	border: 0;
	text-align: start;
	font-size: var(--text-sm);
	color: var(--text-1);
	cursor: pointer;
}
.bn-composer__typeahead-item:hover,
.bn-composer__typeahead-item[aria-selected="true"] {
	background: var(--bn-sunken, #f3f4f6);
	color: var(--bn-accent);
}
.bn-composer__typeahead-prefix {
	color: var(--bn-accent);
	font-weight: 600;
	margin-inline-end: 2px;
}
.bn-composer__typeahead-avatar {
	flex: 0 0 auto;
	width: 28px;
	height: 28px;
	border-radius: var(--r-full, 9999px);
	object-fit: cover;
}
.bn-composer__typeahead-text {
	display: flex;
	flex-direction: column;
	min-width: 0;
	line-height: var(--leading-tight, 1.2);
}
.bn-composer__typeahead-name {
	font-weight: var(--fw-medium, 500);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.bn-composer__typeahead-handle {
	font-size: var(--text-xs);
	color: var(--text-2);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.bn-composer__typeahead-item:hover .bn-composer__typeahead-handle,
.bn-composer__typeahead-item[aria-selected="true"] .bn-composer__typeahead-handle {
	color: inherit;
}

/* Markdown inline code rendered by buddynext_format_content. */
.bn-code {
	font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace);
	font-size: 0.92em;
	padding: 1px 6px;
	background: var(--bn-sunken, color-mix(in oklab, var(--bn-accent) 6%, transparent));
	border-radius: var(--r-sm, 4px);
}
.bn-md-link {
	color: var(--bn-accent);
	text-decoration: underline;
	text-underline-offset: 2px;
}

/* Reactor list popover — opens when a user clicks the per-emoji
   summary chip on a post card. FB-style stacked list of avatars +
   names + emoji badges. The popover is SSR-present per card and
   anchored under its trigger via the relative .reactors-wrap; its
   visibility is reactive (data-wp-bind--hidden from the post-card store). */
.bn-post-card__reactors-wrap {
	position: relative;
	display: inline-flex;
}
.bn-reactors-popover {
	position: absolute;
	inset-block-start: calc(100% + 6px);
	inset-inline-start: 0;
	z-index: 1000;
	min-inline-size: 260px;
	max-inline-size: 340px;
	max-block-size: 380px;
	overflow: auto;
	background: var(--bn-surface, #fff);
	border: 1px solid var(--bn-line, #e5e7eb);
	border-radius: var(--bn-r-md, 8px);
	box-shadow: 0 10px 28px rgba(0, 0, 0, 0.16);
}
.bn-reactors-popover[hidden] { display: none; }
.bn-reactors-popover__head {
	padding: 10px 12px;
	border-block-end: 1px solid var(--bn-line);
	font-size: var(--bn-text-sm);
	font-weight: 600;
	color: var(--bn-ink-2);
}
.bn-reactors-popover__list {
	list-style: none;
	margin: 0;
	padding: var(--bn-s1) 0;
}
.bn-reactors-popover__item {
	display: flex;
	align-items: center;
	gap: var(--bn-s2);
	padding: var(--bn-s2) var(--bn-s3);
}
.bn-reactors-popover__avatar {
	width: 32px;
	height: 32px;
	border-radius: 50%;
	flex-shrink: 0;
}
.bn-reactors-popover__name {
	flex: 1;
	font-size: var(--bn-text-sm);
	color: var(--bn-ink);
}
.bn-reactors-popover__emoji {
	width: 18px;
	height: 18px;
	flex-shrink: 0;
}
.bn-reactors-popover__loading,
.bn-reactors-popover__error {
	padding: var(--bn-s4);
	text-align: center;
	color: var(--bn-ink-3);
	font-size: var(--bn-text-sm);
}

/* Summary trigger button — looks just like the existing chips group
   but adds cursor:pointer + hover wash so it feels clickable. */
.bn-post-card__reactors-trigger {
	background: transparent;
	border: 0;
	padding: 0;
	display: inline-flex;
	gap: var(--bn-s2, 6px);
	align-items: center;
	cursor: pointer;
	border-radius: var(--bn-r-md, 8px);
	transition: background var(--bn-dur-fast, 120ms) var(--bn-ease, ease);
}
.bn-post-card__reactors-trigger:hover {
	background: var(--bn-sunken, color-mix(in oklab, var(--bn-accent) 6%, transparent));
}
/* On hover the trigger shows one unified pill: dissolve each chip's own
   background + border so they merge cleanly instead of stacking a grey blob
   behind bordered chips (the previous double hover effect). */
.bn-post-card__reactors-trigger:hover .bn-post-card__summary-chip {
	background: transparent;
	border-color: transparent;
}
.bn-post-card__reactors-trigger:focus-visible {
	outline: 2px solid var(--bn-accent);
	outline-offset: 2px;
}

/* Realtime "new comment" pill — appears above an open comment thread
   when Pro Soketi delivers comment.added events from other users.
   Smaller and inline (not sticky) since it lives inside a card. */
.bn-comment-new-pill {
	display: block;
	margin: 0 0 var(--bn-s2);
	padding: 6px 12px;
	background: color-mix(in oklab, var(--bn-accent) 12%, transparent);
	color: var(--bn-accent);
	border: 0;
	border-radius: var(--bn-r-md, 8px);
	font-size: var(--text-sm);
	font-weight: 500;
	cursor: pointer;
	text-align: start;
	width: 100%;
}
.bn-comment-new-pill:hover {
	background: color-mix(in oklab, var(--bn-accent) 20%, transparent);
}
.bn-comment-new-pill:focus-visible {
	outline: 2px solid var(--bn-accent);
	outline-offset: 2px;
}

/* Realtime "new posts" pill — appears above the feed when Pro Soketi
   delivers post.new events from other users. Click reloads the feed.
   Position: sticky so it stays visible as the user scrolls within the
   list. */
.bn-feed-new-pill {
	position: sticky;
	top: 8px;
	z-index: 10;
	display: block;
	margin: 0 auto var(--bn-s3);
	padding: var(--bn-s2) var(--bn-s4);
	background: var(--bn-accent, #3b82f6);
	color: var( --bn-accent-fg, #fff );
	border: 0;
	border-radius: var(--bn-r-full, 999px);
	box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
	font-size: var(--text-sm);
	font-weight: 500;
	cursor: pointer;
	transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.bn-feed-new-pill:hover {
	transform: translateY(-1px);
	box-shadow: 0 6px 16px rgba(0, 0, 0, 0.16);
}
.bn-feed-new-pill:focus-visible {
	outline: 2px solid var(--bn-accent);
	outline-offset: 2px;
}

.bn-composer__tools {
	display: flex;
	align-items: center;
	gap: var(--s2);
	padding-top: var(--s3);
	border-top: 1px solid var(--border-soft);
	flex-wrap: wrap;
}

.bn-composer__tool {
	width: 32px;
	height: 32px;
	border: none;
	background: transparent;
	border-radius: var(--r-md);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	color: var(--text-3);
	cursor: pointer;
	transition: background 0.12s, color 0.12s;
}
.bn-composer__tool:hover {
	background: var(--bg-hover);
	color: var(--brand);
	/* Geometry (size/padding/border) is locked once in bn-base.css under .bn-app
	   (theme-proof interactive controls) — base == hover, so no per-state lock. */
}
.bn-composer__tool:focus-visible { outline: 2px solid transparent; outline-offset: 2px; box-shadow: var(--bn-ring); }
.bn-composer__tool .bn-icon,
.bn-composer__tool svg { width: 18px; height: 18px; }


.bn-composer__spacer { flex: 1; }

.bn-composer__privacy {
	display: inline-flex;
	width: auto;
	max-width: 200px;
	height: 32px;
	padding: 0 var(--bn-s6) 0 var(--bn-s3);
	background: var(--bn-sunken);
	border: 1px solid var(--bn-line);
	border-radius: var(--bn-r-md);
	font-family: var(--bn-font-ui);
	font-size: var(--bn-text-xs);
	font-weight: 600;
	color: var(--bn-ink-2);
	cursor: pointer;
	flex-shrink: 0;
}
.bn-composer__privacy:focus-visible {
	outline: none;
	border-color: var(--brand);
}

.bn-composer__submit:disabled,
.bn-composer__submit[disabled] {
	opacity: 0.55;
	cursor: not-allowed;
}

/* Composer drafts — tiny status sitting just before the Share button, inside
 * the .bn-composer__tools flex row (align-items: center). The draft cluster
 * MUST match the toolbar control height (32px). If it is taller, the centered
 * row grows when the draft appears/disappears and the whole composer + feed
 * below it jumps 8px on every draft save/clear — reads as "shaking" while
 * typing in bursts. Keep this height locked to the toolbar baseline. */
.bn-composer__draft {
	display: inline-flex;
	align-items: center;
	gap: var(--bn-s2);
	min-height: 32px;
	font-size: var(--bn-text-xs);
	color: var(--bn-ink-3);
	margin-inline-end: var(--bn-s2);
}

.bn-composer__draft[hidden] { display: none; }

.bn-composer__draft-status {
	font-style: italic;
}

.bn-composer__draft-discard {
	/* Icon button, matching the toolbar tools (32px square) instead of a text
	 * link — consistent with the other composer controls, with a tooltip via
	 * the title/aria-label. (border reserved by the geometry-stability rule.) */
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	padding: 0;
	background: transparent;
	color: var(--text-3);
	cursor: pointer;
	border-radius: var(--r-md);
	transition: background 0.12s, color 0.12s;
}
.bn-composer__draft-discard .bn-icon,
.bn-composer__draft-discard svg { width: 18px; height: 18px; }
.bn-composer__draft-discard:hover { background: var(--bg-hover); color: var(--bn-danger); }
.bn-composer__draft-discard[hidden] { display: none; }

.bn-composer__media-preview {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s2);
}
.bn-composer__media-thumb {
	position: relative;
	width: 80px;
	height: 80px;
	border-radius: var(--r-md);
	overflow: hidden;
	border: 1px solid var(--border);
}
.bn-composer__media-thumb img { width: 100%; height: 100%; object-fit: cover; }
.bn-composer__media-remove {
	position: absolute;
	top: 2px;
	right: 2px;
	width: 20px;
	height: 20px;
	border: none;
	border-radius: var(--r-full);
	background: var(--bn-overlay);
	color: var(--text-on-brand);
	font-size: 14px;
	line-height: 1;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
}
.bn-composer__media-remove:hover { background: var(--red); }

/* Composer link preview — live OG card shown while typing a URL. Mirrors the
   in-feed post link card, with a dismiss affordance. */
.bn-composer__link-preview {
	position: relative;
	display: flex;
	align-items: stretch;
	gap: var(--s2);
	border: 1px solid var(--border);
	border-radius: var(--r-md);
	overflow: hidden;
	background: var(--bg-subtle, var(--surface));
}
.bn-composer__link-card {
	display: flex;
	gap: var(--s2);
	align-items: stretch;
	flex: 1;
	min-width: 0;
	text-decoration: none;
	color: inherit;
}
.bn-composer__link-thumb {
	flex-shrink: 0;
	width: 96px;
	min-height: 72px;
	overflow: hidden;
	background: var(--border);
}
.bn-composer__link-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.bn-composer__link-info {
	display: flex;
	flex-direction: column;
	gap: 2px;
	padding: var(--s2);
	min-width: 0;
}
.bn-composer__link-title {
	font-weight: 600;
	line-height: 1.3;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.bn-composer__link-desc {
	color: var(--text-muted, var(--muted));
	line-height: 1.4;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.bn-composer__link-domain {
	color: var(--text-muted, var(--muted));
	font-size: 0.8em;
}
.bn-composer__link-remove {
	flex-shrink: 0;
	align-self: flex-start;
	margin: var(--s1);
	width: 24px;
	height: 24px;
	border: none;
	border-radius: var(--r-full);
	background: var(--bn-overlay);
	color: var(--text-on-brand);
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
}
.bn-composer__link-remove:hover { background: var(--red); }
.bn-composer__link-remove svg { width: 14px; height: 14px; }

@media (max-width: 640px) {
	.bn-composer__link-thumb { width: 72px; }
}

/* Emoji insert picker — popover of bundled glyph buttons, appended to
   <body> and positioned under the trigger by initEmojiPicker(). */
.bn-emoji-popover {
	display: grid;
	grid-template-columns: repeat(8, 1fr);
	gap: 2px;
	max-width: 320px;
	max-height: 260px;
	overflow-y: auto;
	padding: var(--s2);
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: var(--r-md);
	box-shadow: 0 6px 18px rgba(0, 0, 0, 0.14);
	z-index: 1100;
}
.bn-emoji-popover[hidden] { display: none; }
.bn-emoji-popover__option {
	background: transparent;
	border: 0;
	cursor: pointer;
	line-height: 0;
	padding: var(--bn-s1);
	border-radius: var(--r-md);
	min-width: 32px;
	min-height: 32px;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: transform 150ms ease, background 150ms ease;
}
.bn-emoji-popover__option:hover { transform: scale(1.2); background: var(--bg-hover, rgba(0,0,0,0.05)); }
.bn-emoji-popover__option:focus-visible { outline: 2px solid transparent; box-shadow: inset 0 0 0 2px var(--bn-accent); }
.bn-emoji-popover__option img { width: 22px; height: 22px; display: block; max-width: none; }
.bn-comment__emoji-trigger {
	background: transparent;
	border: 0;
	/* Explicit color so the currentColor SVG icon stays visible on themes (Reign)
	   whose global button color resolves to the button background. */
	color: var(--bn-ink-3);
	cursor: pointer;
	padding: 2px 4px;
	line-height: 0;
	align-self: center;
}
.bn-comment__emoji-trigger img,
.bn-comment__emoji-trigger svg { width: 18px; height: 18px; display: block; }

@media (max-width: 640px) {
	.bn-emoji-popover { grid-template-columns: repeat(6, 1fr); max-width: 260px; }
}

.bn-composer__poll-options {
	display: flex;
	flex-direction: column;
	gap: var(--s2);
}
.bn-composer__poll-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--s2);
}
.bn-composer__poll-question {
	font-size: var(--text-sm);
	font-weight: 600;
	color: var(--text-2);
	margin: 0;
}
/* Poll-cancel reuses the canonical .bn-modal__close primitive (bn-base.css) so
   every composer close button shares one size, token set, and hover state. */
.bn-composer__poll-option {
	width: 100%;
	background: var(--bg-subtle);
	border: 1px solid var(--border);
	border-radius: var(--r-md);
	padding: var(--s2) var(--s3);
	font-family: var(--font-body);
	font-size: var(--text-sm);
	color: var(--text-1);
}
.bn-composer__poll-option:focus { outline: none; border-color: var(--brand); }
.bn-composer__poll-option::placeholder { color: var(--text-3); }

.bn-composer__schedule {
	display: flex;
	align-items: center;
	gap: var(--s2);
	/* No wrap: the datetime input (flex:1 + min-width:0) absorbs the row and
	   shrinks instead, so the × clear button stays inline rather than wrapping
	   to its own line. */
}
.bn-composer__schedule-label {
	display: inline-flex;
	align-items: center;
	gap: var(--s1);
	font-size: var(--text-sm);
	font-weight: 600;
	color: var(--text-2);
	flex-shrink: 0;
}
.bn-composer__schedule-label svg { width: 16px; height: 16px; }
.bn-composer__schedule-input {
	flex: 1 1 auto;
	min-width: 0;
	background: var(--bg-subtle);
	border: 1px solid var(--border);
	border-radius: var(--r-md);
	padding: var(--s2) var(--s3);
	font-family: var(--font-body);
	font-size: var(--text-sm);
	color: var(--text-1);
}
.bn-composer__schedule-input:focus { outline: none; border-color: var(--brand); }
.bn-composer__schedule-clear {
	background: transparent;
	border: 0;
	padding: 2px;
	border-radius: var(--r-sm);
	color: var(--text-3);
	cursor: pointer;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
}
.bn-composer__schedule-clear:hover { color: var(--text-1); background: var(--bg-subtle); }
.bn-composer__schedule-clear svg { width: 16px; height: 16px; }

[data-theme="dark"] .bn-composer__prompt { color: var(--text-1); }
[data-theme="dark"] .bn-composer__privacy { background: var(--bg-subtle); border-color: var(--border); color: var(--text-2); }
[data-theme="dark"] .bn-composer__poll-option { background: var(--bg-subtle); border-color: var(--border); color: var(--text-1); }

/* ── Chip-style privacy selector ─────────────────────────────────────────── */
.bn-composer__tool[data-tone="ai"] { color: var(--bn-accent, var(--brand)); }
.bn-composer__privacy-wrap { position: relative; flex-shrink: 0; }
.bn-composer__privacy-chip {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	background: var(--bn-sunken);
	border: 1px solid var(--bn-line);
	border-radius: var(--bn-r-full, 999px);
	color: var(--bn-ink-3);
	font-family: var(--bn-font-ui);
	font-size: var(--bn-text-xs);
	font-weight: 500;
	padding: 6px 12px;
	cursor: pointer;
	transition: background var(--bn-dur-fast) var(--bn-ease), color var(--bn-dur-fast) var(--bn-ease);
}
.bn-composer__privacy-chip:hover { background: var(--bg-hover); color: var(--bn-ink); }
/* Reserve worst-case width for the variable label ("Everyone" /
   "Followers" / "Just me" / "Space members") so selecting a different
   audience doesn't reflow the composer footer (~48px shift otherwise). */
.bn-composer__privacy-chip strong {
	display: inline-block;
	min-inline-size: 100px;
	color: var(--bn-ink-2);
	font-weight: 600;
	text-align: start;
}
.bn-composer__privacy-caret { display: inline-flex; }
.bn-composer__privacy-caret .bn-icon { width: 12px; height: 12px; }
.bn-composer__privacy-pop {
	position: absolute;
	/* Open downward (below the chip, into the feed) rather than upward: the
	   composer sits at the top of the feed, so an upward popup slid behind the
	   theme's sticky header (higher stacking context) and clipped the options.
	   Downward keeps the full menu in the content area, clear of the header. */
	top: calc(100% + 6px);
	right: 0;
	z-index: 60;
	min-width: 260px;
	margin: 0;
	padding: 6px;
	list-style: none;
	background: var(--bn-surface);
	border: 1px solid var(--bn-line);
	border-radius: var(--bn-r-md);
	box-shadow: var(--bn-shadow-md);
}
.bn-composer__privacy-pop[hidden] { display: none; }
.bn-composer__privacy-pop li { list-style: none; margin: 0; padding: 0; }
.bn-composer__privacy-opt {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	width: 100%;
	padding: 8px 10px;
	border: 0;
	background: transparent;
	border-radius: var(--bn-r-sm);
	text-align: left;
	cursor: pointer;
	color: var(--bn-ink-2);
	transition: background var(--bn-dur-fast) var(--bn-ease);
}
.bn-composer__privacy-opt:hover { background: var(--bg-hover); }
.bn-composer__privacy-opt .bn-icon { width: 16px; height: 16px; flex-shrink: 0; margin-top: 2px; }
.bn-composer__privacy-opt-label { display: flex; flex-direction: column; gap: 1px; }
.bn-composer__privacy-opt-label strong { font-size: var(--bn-text-sm); font-weight: 600; color: var(--bn-ink); }
.bn-composer__privacy-opt-label small { font-size: 11px; color: var(--bn-ink-3); }
li[aria-selected="true"] > .bn-composer__privacy-opt { background: var(--bg-hover); }

/* ── Composer error band ─────────────────────────────────────────────────── */
.bn-composer__error {
	display: flex;
	align-items: center;
	gap: var(--bn-s2);
	padding: var(--bn-s2) var(--bn-s3);
	background: color-mix(in oklch, var(--bn-danger) 12%, var(--bn-surface));
	border: 1px solid color-mix(in oklch, var(--bn-danger) 30%, var(--bn-line));
	border-radius: var(--bn-r-sm);
	color: var(--bn-danger);
	font-size: var(--bn-text-sm);
}
.bn-composer__error[hidden] { display: none; }
.bn-composer__error-text { flex: 1; }
.bn-composer__error-retry {
	border: 0;
	background: transparent;
	color: var(--bn-danger);
	font-weight: 600;
	cursor: pointer;
	text-decoration: underline;
}

/* ── Composer modals (event / voice / AI) ───────────────────────────────── */
.bn-composer-modal[hidden] { display: none !important; }
.bn-composer-modal__body { display: flex; flex-direction: column; gap: var(--bn-s4); }
.bn-composer-modal__field { display: flex; flex-direction: column; gap: 6px; }
.bn-composer-modal__field-label {
	font-size: var(--bn-text-xs);
	font-weight: 600;
	color: var(--bn-ink-3);
	text-transform: uppercase;
	letter-spacing: 0.02em;
}
.bn-composer-modal__row {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: var(--bn-s3);
}
.bn-composer-modal__input,
.bn-composer-modal__textarea {
	width: 100%;
	padding: var(--bn-s2) var(--bn-s3);
	background: var(--bn-sunken);
	border: 1px solid var(--bn-line);
	border-radius: var(--bn-r-md);
	color: var(--bn-ink);
	font: inherit;
}
.bn-composer-modal__input:focus,
.bn-composer-modal__textarea:focus {
	outline: 2px solid var(--bn-accent, var(--brand));
	outline-offset: 2px;
}
.bn-composer-modal__error {
	margin: 0;
	color: var(--bn-danger);
	font-size: var(--bn-text-sm);
}
.bn-composer-modal__error[hidden] { display: none; }

.bn-composer-modal__upsell { text-align: center; padding: var(--bn-s5) var(--bn-s4); }
.bn-composer-modal__upsell-icon {
	display: inline-flex;
	width: 48px;
	height: 48px;
	align-items: center;
	justify-content: center;
	border-radius: 50%;
	background: color-mix(in oklch, var(--bn-accent, var(--brand)) 12%, var(--bn-surface));
	color: var(--bn-accent, var(--brand));
	margin-bottom: var(--bn-s3);
}
.bn-composer-modal__upsell-icon .bn-icon { width: 24px; height: 24px; }
.bn-composer-modal__upsell-title {
	margin: 0 0 6px;
	font-size: var(--bn-text-base);
	font-weight: 600;
	color: var(--bn-ink);
}
.bn-composer-modal__upsell-text { margin: 0 0 var(--bn-s4); color: var(--bn-ink-3); }

/* ── Home feed filter tabs ───────────────────────────────────────────────── */
/* The home feed filter tabs now use the shared .bn-tabs/.bn-tab primitive
   (see templates/feed/home.php) so they match the Home/Explore row and every
   other tab bar - font, focus ring, active state and overflow scroll-fade are
   all inherited. The former bespoke .bn-feed-filter-tab* styles were removed to
   end the duplicate tab language; the .bn-feed-filter-tab class is retained on
   the element only as a JS hook for the feed-tabs store. */

/* ── Feed skeleton + inline error ─────────────────────────────────────────── */
.bn-feed-skeleton[hidden] { display: none; }
.bn-feed-error {
	display: flex;
	align-items: center;
	gap: var(--bn-s3);
	padding: var(--bn-s4);
	background: color-mix(in oklch, var(--bn-danger) 8%, var(--bn-surface));
	border: 1px solid color-mix(in oklch, var(--bn-danger) 25%, var(--bn-line));
	border-radius: var(--bn-r-md);
	color: var(--bn-danger);
	font-size: var(--bn-text-sm);
}
.bn-feed-error[hidden] { display: none; }
.bn-feed-error__icon { display: inline-flex; }
.bn-feed-error__text { flex: 1; }

/* ── Share modal ─────────────────────────────────────────────────────────── */
.bn-share-modal__body { display: flex; flex-direction: column; gap: var(--bn-s3); }
.bn-share-modal__preview {
	display: flex;
	flex-direction: column;
	gap: var(--bn-s1);
	padding: var(--bn-s3);
	border: 1px solid var(--bn-line);
	border-radius: var(--bn-r-md);
	background: var(--bn-sunken);
}
.bn-share-modal__preview[hidden] { display: none; }
.bn-share-modal__preview-author { font-size: var(--bn-text-sm); font-weight: 600; color: var(--bn-ink); }
.bn-share-modal__preview-excerpt { font-size: var(--bn-text-sm); color: var(--bn-ink-2); line-height: var(--leading-snug); }
.bn-share-modal__note {
	width: 100%;
	min-height: 72px;
	padding: var(--bn-s3);
	border: 1px solid var(--bn-line);
	border-radius: var(--bn-r-md);
	background: var(--bn-surface);
	color: var(--bn-ink);
	font-family: var(--font-body);
	font-size: var(--bn-text-sm);
	line-height: var(--leading-normal);
	resize: vertical;
	transition: border-color var(--bn-dur-fast) var(--bn-ease);
}
.bn-share-modal__note:focus { outline: none; border-color: var(--brand); }
.bn-share-modal__note:disabled { opacity: 0.55; cursor: not-allowed; }
.bn-share-modal__actions { display: flex; align-items: center; gap: var(--bn-s2); }
.bn-share-modal__repost { flex: 1; justify-content: center; }
/* Ghost variant rests at a muted ink; lift the Copy-link label to full ink for AA contrast. */
.bn-share-modal__copy { color: var(--bn-ink); }
.bn-share-modal__error { margin: var(--bn-s2) 0 0; color: var(--bn-danger); font-size: var(--bn-text-sm); }
.bn-share-modal__error[hidden] { display: none; }

/* ── Sidebar polish ──────────────────────────────────────────────────────── */
.bn-sidebar-card__header {
	display: flex;
	align-items: baseline;
	gap: 6px;
}
.bn-sidebar-card__caption {
	margin-left: auto;
	font-size: 10px;
	font-weight: 600;
	color: var(--bn-ink-3);
	text-transform: uppercase;
	letter-spacing: 0.04em;
}
.bn-sidebar-card__empty {
	margin: 0;
	padding: var(--bn-s3) 0;
	font-size: var(--bn-text-sm);
	color: var(--bn-ink-3);
}
.bn-sbar-row__unread {
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--bn-accent, var(--brand));
	flex-shrink: 0;
	align-self: center;
}

/* ── Skeleton loading ────────────────────────────────────────────────── */
.bn-skeleton {
	position: relative;
	overflow: hidden;
	background: var(--bn-bg-subtle);
	border-radius: var(--bn-r-md);
}
.bn-skeleton::after {
	content: '';
	position: absolute;
	top: 0;
	left: -100%;
	width: 100%;
	height: 100%;
	background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
	animation: bn-shimmer-slide 1.5s infinite;
}
@keyframes bn-shimmer-slide { to { left: 100%; } }
.bn-skeleton-card {
	padding: var(--bn-s4);
	margin-bottom: var(--bn-s3);
	background: var(--bn-surface);
	border: 1px solid var(--bn-border);
	border-radius: var(--bn-r-lg);
}
.bn-skeleton-avatar { width: 40px; height: 40px; border-radius: 50%; }
.bn-skeleton-line { height: 12px; border-radius: 6px; margin-bottom: 8px; }
.bn-skeleton-line--title { width: 40%; height: 14px; }
.bn-skeleton-line--subtitle { width: 25%; }
.bn-skeleton-line--body { width: 90%; }
.bn-skeleton-line--body-short { width: 60%; }
[data-theme="dark"] .bn-skeleton::after {
	background: linear-gradient(90deg, transparent, rgba(255,255,255,0.05), transparent);
}

/* ── Announcement banner ─────────────────────────────────────────────── */
.bn-announcement {
	display: flex;
	align-items: flex-start;
	gap: var(--s3);
	background: var(--brand-light);
	border: 1px solid var(--brand);
	border-radius: var(--r-md);
	padding: var(--s4);
	font-size: var(--text-sm);
	color: var(--text-1);
}
.bn-announcement__icon { flex-shrink: 0; }
.bn-announcement__body { flex: 1; min-width: 0; }
.bn-announcement__dismiss {
	background: none;
	border: none;
	cursor: pointer;
	color: var(--text-3);
	padding: 0;
	font-size: var(--text-lg);
	line-height: 1;
	flex-shrink: 0;
}
.bn-announcement__dismiss:hover { color: var(--text-1); }

/* ── Feed list ──────────────────────────────────────────────────────────── */
.bn-feed-list { display: flex; flex-direction: column; gap: var(--s4); }

/* ── Load-more wrapper ─────────────────────────────────────────────────────
   The visible control uses .bn-btn[data-variant="secondary"]; this rule just
   centres the button + sized spacing. */
.bn-load-more {
	text-align: center;
	padding: var(--bn-s5) 0;
}
/* "Load more" affordance — a centered pill that reads as a deliberate feed
   action rather than a generic secondary button. Covers the bookmarks pager,
   the no-JS fallback link, and the JS retry control (all .bn-load-more__btn).
   The descendant + [data-variant] selector outranks the .bn-btn[data-variant]
   primitive so the pill height/radius win regardless of stylesheet order. */
.bn-load-more .bn-load-more__btn[data-variant] {
	min-width: 200px;
	height: 44px;
	border-radius: var(--bn-r-full, 999px);
	font-weight: var(--bn-fw-semibold);
	box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
	transition: background var(--bn-dur) var(--bn-ease),
				border-color var(--bn-dur) var(--bn-ease),
				color var(--bn-dur) var(--bn-ease),
				box-shadow var(--bn-dur) var(--bn-ease),
				transform var(--bn-dur-fast) var(--bn-ease);
}
.bn-load-more .bn-load-more__btn[data-variant]:hover {
	border-color: var(--bn-accent);
	color: var(--bn-accent);
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
	transform: translateY(-1px);
}
.bn-load-more__spinner {
	display: flex;
	justify-content: center;
}
.bn-load-more__spinner-line {
	display: block;
	width: 120px;
	height: 14px;
	border-radius: var(--bn-r-sm);
}

/* ── Inner-template mobile (≤640px) ─────────────────────────────────────────
   Shell handles rail + right-sidebar collapse; inner overrides only tighten
   composer padding + tools wrapping at small widths. */
@media (max-width: 640px) {
	.bn-composer { padding: var(--s3) var(--s3) var(--s2); }
	.bn-composer__tools { gap: var(--s1); }
	.bn-composer__privacy { font-size: 10px; padding: 0 var(--s2); }
}

/* ── End-of-feed marker ────────────────────────────────────────────────────
   Rendered in place of the infinite-scroll sentinel once the API returns no
   next_cursor. Quiet, single line — not a CTA. */
.bn-feed-end {
	text-align: center;
	padding: var(--bn-s5) 0 var(--bn-s6);
	color: var(--bn-ink-3);
	font-size: var(--text-sm);
}
.bn-feed-end__text {
	display: inline-block;
	padding: var(--bn-s2) var(--bn-s4);
	border-radius: var(--bn-r-md);
	background: var(--bn-surface-2, var(--surface));
	border: 1px solid var(--bn-border, var(--border));
}
.bn-load-more__spinner-text {
	display: block;
	margin-top: var(--bn-s2);
	color: var(--bn-ink-3);
	font-size: var(--text-sm);
}

/* ── Post-card timestamp link ─────────────────────────────────────────────
   The relative-time becomes a permalink to /p/{id}/. Visual style stays
   identical to the bare <time> — no underline, inherit color — until hover. */
.bn-post-card__time-link {
	color: inherit;
	text-decoration: none;
}
.bn-post-card__time-link:hover .bn-post-card__time,
.bn-post-card__time-link:focus-visible .bn-post-card__time {
	color: var(--bn-text-1, var(--text-1));
	text-decoration: underline;
	text-underline-offset: 2px;
}

/* ── Single-post permalink page ───────────────────────────────────────────
   Renders inside the shell main column. Breadcrumb sits flush with the post
   card; thread heading separates the comment region. */
.bn-single-post {
	display: flex;
	flex-direction: column;
	gap: var(--bn-s4);
}
.bn-single-post__breadcrumb {
	font-size: var(--text-sm);
	color: var(--bn-ink-3);
}
.bn-single-post__crumbs {
	display: flex;
	flex-wrap: wrap;
	gap: var(--bn-s2);
	list-style: none;
	padding: 0;
	margin: 0;
	align-items: center;
}
.bn-single-post__crumbs a {
	color: var(--bn-ink-3);
	text-decoration: none;
}
.bn-single-post__crumbs a:hover,
.bn-single-post__crumbs a:focus-visible {
	color: var(--bn-brand, var(--brand));
	text-decoration: underline;
	text-underline-offset: 2px;
}
.bn-single-post__crumb-sep {
	color: var(--bn-border-strong, var(--border));
}
.bn-single-post__thread {
	margin-top: var(--bn-s4);
}
.bn-single-post__thread-heading {
	font-size: var(--text-lg);
	font-weight: 600;
	margin: 0 0 var(--bn-s2);
}
.bn-single-post__thread-hint {
	color: var(--bn-ink-3);
	font-size: var(--text-sm);
	margin: 0 0 var(--bn-s4);
}
.bn-single-post--missing { padding: var(--bn-s6) 0; }

/* ── Bookmarks hub ─────────────────────────────────────────────────────────
   Header block introduces the surface and is the only structural difference
   from the home feed — cards reuse the canonical .bn-feed-list grid. */
.bn-bookmarks__header {
	margin-bottom: var(--bn-s4);
}
.bn-bookmarks__title {
	font-size: var(--text-2xl);
	font-weight: 700;
	margin: 0 0 var(--bn-s2);
	color: var(--bn-text-1, var(--text-1));
}
.bn-bookmarks__lead {
	color: var(--bn-ink-3);
	margin: 0;
	font-size: var(--text-sm);
}
.bn-bookmarks__list {
	display: flex;
	flex-direction: column;
	gap: var(--bn-s4);
}

/* ── Mobile (≤640px) ── */
@media (max-width: 640px) {
	.bn-single-post__crumbs { font-size: var(--text-xs); }
	.bn-bookmarks__title    { font-size: var(--text-xl); }
}

/* ── 390px mobile pass (Wave 2 E) ──────────────────────────────────────────
 * The wave-1 surfaces (/p/{id}/, /me/bookmarks/, composer modals) all need
 * to remain usable on a 390px viewport without horizontal scroll and
 * without burying primary CTAs below the fold. Each rule here exists to
 * fix a real bug spotted during the wave-2 mobile walk.
 */
@media (max-width: 480px) {
	/* Single post: tighter padding + stacked breadcrumb so the post card
	   reaches edge-to-edge. */
	.bn-single-post {
		padding: var(--bn-s3) var(--bn-s2);
	}
	.bn-single-post__breadcrumb {
		font-size: var(--bn-text-xs);
	}
	.bn-single-post__thread-heading {
		font-size: var(--bn-text-base);
	}

	/* Bookmarks: cards already stack via the existing list layout but the
	   wrapper padding needs a 16px gutter and the header label can wrap. */
	.bn-bookmarks {
		padding: var(--bn-s3) var(--bn-s2);
	}
	.bn-bookmarks__title {
		font-size: var(--bn-text-lg);
		line-height: 1.3;
	}
	.bn-bookmarks__lead {
		font-size: var(--bn-text-sm);
	}

	/* Composer modals: full-width minus 16px gutter so the panel is not
	   trapped inside the desktop max-width. Buttons stack and the submit
	   stays sticky so users do not lose it below the keyboard. */
	.bn-modal__panel,
	.bn-modal__panel[data-size="lg"],
	.bn-modal__panel[data-size="xl"] {
		max-width: calc(100vw - 16px);
		width: calc(100vw - 16px);
		max-height: calc(100vh - 32px);
		margin: var(--bn-s4) var(--bn-s2);
	}
	.bn-composer-modal__body {
		gap: var(--bn-s3);
	}
	.bn-composer-modal__row {
		flex-direction: column;
		align-items: stretch;
		gap: var(--bn-s2);
	}
	.bn-modal__foot,
	.bn-composer-modal__foot {
		position: sticky;
		bottom: 0;
		background: var(--bn-surface);
		padding-block: var(--bn-s2);
	}
	.bn-modal__foot .bn-btn,
	.bn-composer-modal__foot .bn-btn {
		width: 100%;
	}
}
