/* Phase 4 layout shell.
 *
 * Two top-level layout modes — signed-out (centered card) and signed-in
 * (header + content + nav). Mobile-first; desktop layouts kick in at the
 * width breakpoint.
 */

.app {
	min-height: 100vh;
	min-height: 100dvh;
}

/* "New version available — reload" banner. Sits above #app so route
   navigation can't unmount it; only a hard reload clears it. */
.app-update-banner {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--s-3);
	padding: var(--s-2) var(--s-4);
	background: var(--c-accent-muted, var(--c-bg-raised));
	color: var(--c-fg);
	border-bottom: 1px solid var(--c-accent);
	font-size: var(--t-sm, 0.875rem);
	text-align: center;
}
.app-update-banner button {
	font-weight: 600;
}

/* Build stamp at the foot of Settings. Quiet, monospace, no chrome. */
.settings-footer {
	margin-top: var(--s-8);
	padding-top: var(--s-4);
	border-top: 1px solid var(--c-border);
	color: var(--c-fg-muted);
	font-size: var(--t-sm, 0.875rem);
	text-align: center;
}

/* Admin view — hidden bookmark-only route. Three-stat header, then
   one row per user with avatar + identity + timestamps + counts. */
.view-admin .view-title {
	margin-bottom: var(--s-4);
}
.admin-stats {
	display: flex;
	gap: var(--s-3);
	margin-bottom: var(--s-6);
	flex-wrap: wrap;
}
.admin-stat {
	flex: 1;
	min-width: 6rem;
	padding: var(--s-3);
	border: 1px solid var(--c-border);
	border-radius: var(--r-1);
	text-align: center;
}
.admin-stat-value {
	font-family: var(--f-display, var(--f-serif));
	font-size: var(--t-xl, 1.5rem);
	font-weight: 600;
	color: var(--c-fg);
}
.admin-stat-label {
	color: var(--c-fg-muted);
	font-size: var(--t-sm, 0.875rem);
	margin-top: var(--s-1);
}
.admin-user-list {
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
}
.admin-user-row {
	display: grid;
	grid-template-columns: 40px 1fr auto;
	grid-template-rows: auto auto;
	gap: var(--s-1) var(--s-3);
	align-items: center;
	padding: var(--s-3);
	border: 1px solid var(--c-border);
	border-radius: var(--r-1);
}
.admin-user-pic {
	grid-row: 1 / span 2;
	width: 40px;
	height: 40px;
	border-radius: 50%;
	object-fit: cover;
	background: var(--c-bg-raised);
}
.admin-user-pic.placeholder {
	border: 1px solid var(--c-border);
}
.admin-user-id {
	grid-column: 2;
	grid-row: 1;
	min-width: 0;
}
.admin-user-name {
	font-weight: 600;
	display: flex;
	align-items: center;
	gap: var(--s-2);
}
.admin-user-email {
	font-size: var(--t-sm, 0.875rem);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.admin-user-times {
	grid-column: 3;
	grid-row: 1;
	font-size: var(--t-sm, 0.875rem);
	color: var(--c-fg-muted);
	text-align: right;
}
.admin-user-counts {
	grid-column: 2 / span 2;
	grid-row: 2;
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-3);
	font-size: var(--t-sm, 0.875rem);
	color: var(--c-fg-muted);
}
.admin-user-counts .mono {
	color: var(--c-fg);
}
.tag-admin {
	display: inline-block;
	padding: 0 var(--s-2);
	border: 1px solid var(--c-accent);
	border-radius: var(--r-1);
	color: var(--c-accent);
	font-family: var(--f-mono);
	font-size: var(--t-xs, 0.75rem);
	text-transform: uppercase;
}

/* Recommendations view — same row chrome as the library, plus a
   muted "because you liked X, Y" attribution under the meta line.
   Hidden bookmark-only route at #/recs. */
.view-sub {
	margin-bottom: var(--s-4);
	font-size: var(--t-sm, 0.875rem);
}
.recs-because {
	margin-top: var(--s-1);
	color: var(--c-fg-muted);
	font-size: var(--t-sm, 0.875rem);
}
.recs-because em {
	font-style: italic;
	color: var(--c-fg);
}

/* Phase B "discover" badge on rec rows whose book isn't yet in the user's
   library — sourced from a curated external list (r/Fantasy Top Novels,
   NYT Notable, etc.). Lazily resolved into a Bookie Book row on click. */
.recs-discover-badge {
	display: inline-block;
	margin-left: var(--s-2);
	padding: 0 var(--s-2);
	font-size: var(--t-xs, 0.75rem);
	color: var(--c-fg-muted);
	border: 1px solid var(--c-line);
	border-radius: 2px;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	vertical-align: middle;
}
/* Phase-B test marker. Visually distinct from .recs-discover-badge so
   we can tell at a glance which recs are external-curated vs Phase A
   from an author we don't follow. Keep until we're done validating
   the rec modal's two phases. */
.recs-ext-badge {
	display: inline-block;
	margin-left: var(--s-2);
	padding: 0 var(--s-2);
	font-size: var(--t-xs, 0.75rem);
	color: var(--c-accent, #c78a3a);
	border: 1px dashed var(--c-accent, #c78a3a);
	border-radius: 2px;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	vertical-align: middle;
}
.external-rec-modal .book-modal-actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-3);
	align-items: center;
	margin-top: var(--s-4);
}

/* Unified rec modal — the action-oriented modal opened from the
   rec list (both Phase A and Phase B). Distinct from .book-modal
   used by the library / releases / author pages, which is the full
   "manage this book" surface. The rec modal is for evaluating a
   recommendation and triggering one of a handful of clean actions
   (TBR / read / follow / dismiss / view externally / close). */
/* A's book-page action stack: a full-width brass primary, quiet
   left-aligned secondary actions, then the dismiss zone set apart with the
   single oxblood left edge. Vertical, not a wrap row. */
.rec-modal-actions {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: var(--s-3);
	margin-top: var(--s-5);
	padding-top: var(--s-5);
	border-top: 1px solid var(--c-border);
}
.rec-modal-actions .primary {
	width: 100%;
	padding: var(--s-3);
	font-size: var(--t-md);
	background: var(--c-accent);
	border: 1px solid var(--c-accent);
	color: var(--c-bg);
	font-weight: 500;
	border-radius: var(--r-2);
}
.rec-modal-actions .primary:hover:not(:disabled) {
	background: var(--c-accent-hover);
	border-color: var(--c-accent-hover);
}
.rec-modal-actions > button:not(.primary),
.rec-modal-actions > a.linkish {
	text-align: left;
	align-self: flex-start;
}
.rec-modal-spacer {
	display: none;
}
.rec-modal-dismiss-group {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--s-2);
	margin-top: var(--s-2);
	padding: var(--s-3);
	border-left: 2px solid var(--c-oxblood);
	background: var(--c-surface);
}
.rec-modal-dismiss {
	color: var(--c-fg-muted);
	font-size: var(--t-base);
}
.rec-modal-dismiss:hover {
	color: var(--c-fg);
}
/* Secondary "...and hide similar": dimmer to mark it the stronger,
   less-common choice (people fat-finger the plain dismiss). */
.rec-modal-dismiss-similar {
	color: var(--c-fg-faint);
	font-size: var(--t-sm);
}
.rec-modal-dismiss-similar:hover {
	color: var(--c-fg-muted);
}
.rec-modal-followed {
	color: var(--c-fg-muted);
	cursor: default;
	text-align: left;
	align-self: flex-start;
}
.rec-modal-confirm {
	margin-top: var(--s-4);
	padding: var(--s-3) var(--s-3);
	border-top: 1px solid var(--c-border);
	color: var(--c-fg-muted);
	font-style: italic;
	text-align: center;
}
/* Mark-as-read inline expander. Lives in the action row when the
   user taps "mark as read"; replaces the button with a small rating
   + finished-date affordance + cancel / save. Whole-star granularity
   only (no half-stars) — fine-grained editing belongs in the library
   modal, not here. */
.rec-modal-rate {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--s-3);
	padding: var(--s-2) var(--s-3);
	border: 1px solid var(--c-border);
	border-radius: 4px;
	background: var(--c-bg-soft, transparent);
}
.rec-modal-rate-label {
	font-size: 0.85rem;
}
.rec-modal-stars .star {
	font-size: 1.2rem;
}
.rec-modal-finished {
	display: inline-flex;
	align-items: center;
	gap: var(--s-2);
	font-size: 0.85rem;
}
.rec-modal-finished input[type='date'] {
	background: transparent;
	color: var(--c-fg);
	border: 1px solid var(--c-border);
	border-radius: 3px;
	padding: 2px 4px;
}
.rec-modal-rate-actions {
	display: inline-flex;
	gap: var(--s-2);
	align-items: center;
	margin-left: auto;
}

/* Book-page treatment for the rec modal (Phase 2 step 8). Scoped to
   .rec-modal so the library book modal is untouched: a larger cover with a
   spine edge, the title in Newsreader (not the wordmark face), a roomier
   description, and the "because you loved …" line as a quiet footnote. */
.rec-modal .book-modal-cover {
	width: 96px;
	height: 144px;
	border-left: 3px solid var(--c-accent-dim);
	border-radius: 0 var(--r-2) var(--r-2) 0;
	box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5);
}
.rec-modal .book-modal-title {
	font-family: var(--f-serif);
	font-size: var(--t-xl);
	font-variation-settings: 'opsz' 36;
	line-height: var(--lh-tight);
}
.rec-modal .book-modal-description {
	font-size: var(--t-md);
	line-height: var(--lh-body);
	color: var(--c-fg);
}
.rec-modal-eyebrow {
	display: block;
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--c-fg-dim);
	margin-bottom: var(--s-2);
}
.rec-modal-tags {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-2);
	margin: var(--s-4) 0 var(--s-5);
}
.rec-modal-tag {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--c-fg-muted);
	border: 1px solid var(--c-border);
	border-radius: var(--r-1);
	padding: 2px var(--s-2);
}
.rec-modal-because {
	margin: var(--s-4) 0 0;
	padding-top: var(--s-3);
	border-top: 1px solid var(--c-border);
	font-size: var(--t-sm);
	color: var(--c-fg-faint);
}
.rec-modal-because em {
	font-style: italic;
	color: var(--c-fg-muted);
}

/* Recs view controls: 4-mode segmented selector + refresh action.
   Mode selector lets the user pick what kind of recs to see
   (discover / mixed / followed / all). Cache-age hint + refresh
   button sit to the right of the mode group. */
/* Tune trigger sits on its own row above the controls, right-aligned. */
.recs-header {
	display: flex;
	align-items: center;
	justify-content: flex-end;
	margin: var(--s-3) 0;
}
.recs-controls {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--s-3);
	margin: 0 0 var(--s-4);
}
/* Mode selector — a "showing: …" dropdown that names the current choice in
   plain language and opens to four described options (Phase 2 step 8). */
.recs-mode-select {
	appearance: none;
	flex: 1;
	min-width: 0;
	display: flex;
	align-items: center;
	gap: var(--s-3);
	background: var(--c-surface);
	border: 1px solid var(--c-border);
	border-radius: var(--r-2);
	color: var(--c-fg);
	padding: var(--s-2) var(--s-3);
	font-family: inherit;
	text-align: left;
	cursor: pointer;
}
.recs-mode-select:hover,
.recs-mode-select.open {
	border-color: var(--c-border-strong);
}
.recs-mode-key {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--c-fg-dim);
	flex-shrink: 0;
}
.recs-mode-now {
	flex: 1;
	min-width: 0;
	display: flex;
	flex-direction: column;
	line-height: 1.25;
}
.recs-mode-now-label {
	font-family: var(--f-serif);
	font-size: var(--t-md);
	color: var(--c-fg);
}
.recs-mode-now-desc {
	font-size: var(--t-sm, 0.875rem);
	color: var(--c-fg-muted);
}
.recs-mode-caret {
	color: var(--c-fg-dim);
	flex-shrink: 0;
}
.recs-mode-list {
	margin: var(--s-2) 0 var(--s-4);
	border: 1px solid var(--c-border-strong);
	border-radius: var(--r-2);
	background: var(--c-surface-2);
	overflow: hidden;
}
.recs-mode-option {
	appearance: none;
	width: 100%;
	display: flex;
	align-items: center;
	gap: var(--s-3);
	background: transparent;
	border: 0;
	border-bottom: 1px solid var(--c-border);
	color: var(--c-fg);
	padding: var(--s-3);
	font-family: inherit;
	text-align: left;
	cursor: pointer;
}
.recs-mode-option:last-child {
	border-bottom: 0;
}
.recs-mode-option:hover {
	background: var(--c-surface);
}
.recs-mode-option.active {
	box-shadow: inset 3px 0 0 var(--c-accent);
	background: var(--c-surface);
}
.recs-mode-option-text {
	flex: 1;
	min-width: 0;
	display: flex;
	flex-direction: column;
	line-height: 1.3;
}
.recs-mode-option-label {
	font-family: var(--f-serif);
	font-size: var(--t-md);
}
.recs-mode-option.active .recs-mode-option-label {
	color: var(--c-accent-hover);
}
.recs-mode-option-desc {
	font-size: var(--t-sm, 0.875rem);
	color: var(--c-fg-muted);
}
.recs-mode-check {
	color: var(--c-accent);
	flex-shrink: 0;
}
.recs-year-filter select {
	appearance: none;
	background: transparent;
	border: 1px solid var(--c-line);
	border-radius: 4px;
	color: var(--c-fg-muted);
	padding: var(--s-1) calc(var(--s-3) + 0.75em) var(--s-1) var(--s-3);
	font-size: var(--t-sm, 0.875rem);
	font-family: inherit;
	cursor: pointer;
	/* Caret. Inline SVG so we don't depend on a font icon set. */
	background-image:
		linear-gradient(45deg, transparent 50%, var(--c-fg-muted) 50%),
		linear-gradient(135deg, var(--c-fg-muted) 50%, transparent 50%);
	background-position:
		right 0.7em top 50%,
		right 0.4em top 50%;
	background-size:
		0.35em 0.35em,
		0.35em 0.35em;
	background-repeat: no-repeat;
}
.recs-year-filter select:hover {
	color: var(--c-fg);
	border-color: var(--c-fg);
}
.recs-refresh {
	display: inline-flex;
	align-items: center;
	gap: var(--s-2);
	font-size: var(--t-sm, 0.875rem);
	color: var(--c-fg-muted);
}
.recs-cache-age {
	font-variant-numeric: tabular-nums;
}
/* In-flight hint in the tune-sheet footer. Brass + a gentle pulse so a filter
   change reads as "working" even though the list reloads behind the sheet. */
.recs-recomputing {
	color: var(--c-accent);
	animation: recs-recompute-pulse 1.1s ease-in-out infinite;
}
@keyframes recs-recompute-pulse {
	0%,
	100% {
		opacity: 0.55;
	}
	50% {
		opacity: 1;
	}
}

/* ──────────────── genre filter + tags (Phase 2 step 7) ──────────────── */
/* A second control row under .recs-controls — toggle chips that wrap onto
   their own line. Active uses the brass accent (vs the mode group's
   paper-white fill) so "filtering by genre" reads distinct from the mode
   pick. Deliberately basic; step 8 folds all knobs into the tune-popup. */
.recs-genre-filter {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--s-2);
	margin: 0 0 var(--s-4);
}
.recs-genre-chip {
	appearance: none;
	background: transparent;
	border: 1px solid var(--c-line);
	border-radius: 2px;
	color: var(--c-fg-muted);
	padding: var(--s-1) var(--s-2);
	font-size: var(--t-xs, 0.75rem);
	font-family: inherit;
	letter-spacing: 0.03em;
	cursor: pointer;
	user-select: none;
	transition:
		color var(--motion-fast, 120ms ease),
		border-color var(--motion-fast, 120ms ease);
}
.recs-genre-chip:hover {
	color: var(--c-fg);
	border-color: var(--c-fg-muted);
}
.recs-genre-chip.active {
	background: var(--c-accent, #c78a3a);
	border-color: var(--c-accent, #c78a3a);
	color: var(--c-bg, #0e0a06);
	font-weight: 500;
}
.recs-genre-clear {
	margin-left: var(--s-1);
	font-size: var(--t-xs, 0.75rem);
}

/* Genre display tags — quiet classification metadata on rec cards + in the
   modal. Borderless small-caps so they don't compete with the interactive
   filter chips above or the discover/ext badges. */
.recs-genre-tags {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-1) var(--s-3);
	margin-top: var(--s-1);
}
.recs-genre-tag {
	font-size: var(--t-xs, 0.75rem);
	color: var(--c-fg-dim, var(--c-fg-muted));
	letter-spacing: 0.06em;
	text-transform: uppercase;
}
.book-modal-genres {
	display: inline-flex;
	flex-wrap: wrap;
	gap: var(--s-3);
	margin-left: var(--s-2);
}

/* ─── tune-popup (Phase 2 step 8) ───
   One "filter & tune" trigger collapses the secondary filters + scoring
   knobs into a single sheet so the main /recs surface stays a clean list.
   The trigger carries a brass count badge when filters are active. */
.recs-tune-toggle {
	appearance: none;
	display: inline-flex;
	align-items: center;
	gap: var(--s-2);
	background: transparent;
	border: 1px solid var(--c-accent);
	border-radius: var(--r-2);
	color: var(--c-accent);
	padding: var(--s-2) var(--s-3);
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.12em;
	text-transform: uppercase;
	cursor: pointer;
	white-space: nowrap;
}
.recs-tune-toggle:hover,
.recs-tune-toggle.active {
	background: color-mix(in srgb, var(--c-accent) 14%, transparent);
}
.recs-tune-icon {
	width: 0.95rem;
	height: 0.95rem;
	flex-shrink: 0;
}
.recs-tune-count {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 1.1rem;
	height: 1.1rem;
	padding: 0 0.3em;
	background: var(--c-accent);
	color: var(--c-bg);
	border-radius: var(--r-1);
	font-size: var(--t-xs, 0.75rem);
	font-weight: 600;
}
.recs-tune-sheet {
	margin: 0 0 var(--s-4);
	border: 1px solid var(--c-border-strong);
	border-radius: var(--r-2);
	background: var(--c-surface-2);
	padding: var(--s-3) var(--s-4) var(--s-4);
}
.recs-tune-handle {
	width: 2rem;
	height: 3px;
	border-radius: 2px;
	background: var(--c-border-strong);
	margin: 0 auto var(--s-3);
}
.recs-tune-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	padding-bottom: var(--s-3);
	margin-bottom: var(--s-2);
	border-bottom: 1px solid var(--c-border);
}
.recs-tune-title {
	font-family: var(--f-serif);
	font-size: var(--t-md);
	color: var(--c-fg);
}
.recs-tune-reset {
	font-size: var(--t-sm, 0.875rem);
}
.recs-tune-section {
	padding: var(--s-3) 0;
	border-top: 1px solid var(--c-border);
}
.recs-tune-section:first-child {
	padding-top: 0;
	border-top: 0;
}
.recs-tune-label {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--c-fg-dim);
	margin-bottom: var(--s-3);
	display: flex;
	justify-content: space-between;
	align-items: baseline;
}
.recs-tune-val {
	font-family: var(--f-mono);
	color: var(--c-accent);
	letter-spacing: 0;
	text-transform: none;
}
.recs-tune-chips {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-2);
}
.recs-tune-chip {
	appearance: none;
	background: transparent;
	border: 1px solid var(--c-line);
	border-radius: var(--r-1);
	color: var(--c-fg-muted);
	padding: var(--s-1) var(--s-2);
	font-size: var(--t-xs, 0.75rem);
	font-family: inherit;
	cursor: pointer;
}
.recs-tune-chip:hover {
	color: var(--c-fg);
	border-color: var(--c-fg-muted);
}
.recs-tune-chip.active {
	background: var(--c-accent);
	border-color: var(--c-accent);
	color: var(--c-bg);
	font-weight: 500;
}
.recs-tune-range {
	width: 100%;
	accent-color: var(--c-accent);
	margin: var(--s-1) 0;
}
.recs-tune-ends {
	display: flex;
	justify-content: space-between;
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	color: var(--c-fg-faint);
}
/* published / seed rows: label left, dropdown right */
.recs-tune-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--s-3);
}
.recs-tune-row .recs-tune-label {
	margin-bottom: 0;
}
.recs-tune-select {
	appearance: none;
	background: var(--c-surface);
	border: 1px solid var(--c-border-strong);
	border-radius: var(--r-2);
	color: var(--c-fg);
	padding: var(--s-2) calc(var(--s-4) + 0.75em) var(--s-2) var(--s-3);
	font-size: var(--t-base);
	font-family: inherit;
	cursor: pointer;
	background-image:
		linear-gradient(45deg, transparent 50%, var(--c-accent) 50%),
		linear-gradient(135deg, var(--c-accent) 50%, transparent 50%);
	background-position:
		right 0.85em top 55%,
		right 0.55em top 55%;
	background-size:
		0.35em 0.35em,
		0.35em 0.35em;
	background-repeat: no-repeat;
}
/* toggle row — two-line label (title + description) + a pill switch */
.recs-tune-switch {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--s-4);
	padding: var(--s-3) 0;
	border-top: 1px solid var(--c-border);
	cursor: pointer;
}
.recs-tune-switch-text {
	display: flex;
	flex-direction: column;
	gap: 2px;
}
.recs-tune-switch-title {
	font-size: var(--t-base);
	color: var(--c-fg);
}
.recs-tune-switch-desc {
	font-size: var(--t-sm, 0.875rem);
	color: var(--c-fg-muted);
}
.recs-tune-pill {
	appearance: none;
	flex-shrink: 0;
	width: 2.4rem;
	height: 1.3rem;
	border-radius: 1rem;
	background: var(--c-border-strong);
	position: relative;
	cursor: pointer;
	transition: background var(--motion-fast, 120ms ease);
}
.recs-tune-pill::after {
	content: '';
	position: absolute;
	top: 2px;
	left: 2px;
	width: calc(1.3rem - 4px);
	height: calc(1.3rem - 4px);
	border-radius: 50%;
	background: var(--c-fg-faint);
	transition: transform var(--motion-fast, 120ms ease);
}
.recs-tune-pill:checked {
	background: var(--c-accent);
}
.recs-tune-pill:checked::after {
	transform: translateX(1.1rem);
	background: var(--c-bg);
}
.recs-tune-foot {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	padding-top: var(--s-4);
	border-top: 1px solid var(--c-border);
	font-size: var(--t-sm, 0.875rem);
	color: var(--c-fg-muted);
}
.recs-tune-done {
	appearance: none;
	margin-left: auto;
	background: var(--c-accent);
	border: 1px solid var(--c-accent);
	border-radius: var(--r-2);
	color: var(--c-bg);
	font-weight: 500;
	padding: var(--s-2) var(--s-5);
	font-family: inherit;
	font-size: var(--t-sm, 0.875rem);
	cursor: pointer;
}
.recs-tune-done:hover {
	background: var(--c-accent-hover);
	border-color: var(--c-accent-hover);
}
.recs-tune-done:hover {
	border-color: var(--c-accent);
	color: var(--c-accent);
}

/* ──────────────── signed-out ──────────────── */

.app.signed-out .signin-shell {
	max-width: var(--content-max);
	margin: 0 auto;
	padding: var(--s-10) var(--s-5) var(--s-12);
}
.brand {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--s-2);
	margin-bottom: var(--s-10);
}
.brand h1 {
	font-family: var(--f-display);
	font-size: var(--t-2xl);
	font-weight: 600;
	letter-spacing: -0.02em;
	/* Matches the signed-in shell wordmark — same opsz / SOFT / WONK
	 * so the letterforms read identically; only the rendered size
	 * differs. (Earlier mismatch let Fraunces' optical-size axis
	 * pull the two wordmarks into different characters at different
	 * sizes — looked like two slightly different fonts.) */
	font-variation-settings:
		'opsz' 48,
		'SOFT' 50,
		'WONK' 1;
}
.signin {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--s-5);
	padding: var(--s-8) var(--s-6);
	border: 1px solid var(--c-border);
	border-radius: var(--r-2);
	background: var(--c-surface);
}
.signin p {
	max-width: 28ch;
	text-align: center;
	color: var(--c-fg-muted);
	font-size: var(--t-base);
}
/* The pitch paragraph — three sentence fragments, one per feature.
 * Wider max-width than the prior single-line tagline so the three
 * fragments breathe; slight italic on a single phrase would be one
 * step too marketing-y. Sentences carry their own weight. */
.signin .signin-pitch {
	max-width: 40ch;
	line-height: 1.55;
}
.signin-connection-error {
	margin-bottom: var(--s-6);
	padding: var(--s-3) var(--s-4);
	border: 1px solid var(--c-border);
	border-left: 3px solid var(--c-accent);
	border-radius: var(--r-1);
	background: var(--c-surface);
	color: var(--c-fg-muted);
	font-size: var(--t-sm);
	text-align: center;
}

/* ──────────────── signed-out chapbook shell ────────────────
   The prose-direction layout: cover plate, then sign-in (above the
   fold so returning users don't have to scroll past an explainer),
   then overview prose, then a guide-link row, then the colophon
   footer. Matches the visual vocabulary of /help and /about so the
   three pages read as one bound volume. */

.app.signed-out .chapbook-shell {
	max-width: 32rem; /* a hair tighter than --content-max; one signature, not the whole pamphlet */
	margin: 0 auto;
	padding: var(--s-12) var(--s-6) var(--s-8);
}

.cover {
	text-align: center;
	padding: var(--s-8) 0 var(--s-10);
	border-bottom: 1px solid var(--c-border);
	position: relative;
}
.cover::before {
	content: '';
	position: absolute;
	left: 50%;
	top: var(--s-2);
	transform: translateX(-50%);
	width: var(--s-10);
	height: 1px;
	background: var(--c-border-strong, var(--c-border));
}
.cover .kicker {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.32em;
	text-transform: uppercase;
	color: var(--c-fg-dim);
	margin-bottom: var(--s-6);
}
.cover .kicker .glyph {
	color: var(--c-accent);
	margin: 0 var(--s-2);
}
.cover .wordmark {
	font-family: var(--f-display);
	font-variation-settings:
		'opsz' 120,
		'SOFT' 50,
		'WONK' 1;
	font-weight: 600;
	font-size: clamp(3rem, 11vw, 4.75rem);
	line-height: 0.95;
	letter-spacing: -0.02em;
	color: var(--c-fg);
	margin: 0;
}
.cover .tagline {
	font-family: var(--f-serif);
	font-variation-settings: 'opsz' 36;
	font-style: italic;
	font-weight: 400;
	font-size: var(--t-md);
	color: var(--c-fg-muted);
	line-height: 1.35;
	max-width: 22rem;
	margin: var(--s-6) auto 0;
	text-wrap: balance;
}

/* Sign-in section sits directly after the cover. The existing .signin
   card styles are reused; this just adds the kicker line above it
   ("Sign in" with a hairline that fills the rest of the width). */
.signin-section {
	padding: var(--s-8) 0 var(--s-6);
}
.signin-kicker {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.28em;
	text-transform: uppercase;
	color: var(--c-fg-dim);
	margin-bottom: var(--s-5);
	display: flex;
	align-items: center;
	gap: var(--s-3);
}
.signin-kicker .glyph {
	color: var(--c-accent);
}
.signin-kicker .bar {
	flex: 1;
	height: 1px;
	background: var(--c-border);
}
.signin .signin-note {
	max-width: 32ch;
	text-align: center;
	color: var(--c-fg-muted);
	font-family: var(--f-serif);
	font-variation-settings: 'opsz' 18;
	font-size: var(--t-sm);
	line-height: 1.35;
	font-style: italic;
}

/* Overview prose. Drop cap on the opening paragraph, same handle as
   the zine + about page. */
.overview {
	padding: var(--s-10) 0 var(--s-8);
	border-top: 1px solid var(--c-border);
}
.overview .prose p {
	font-family: var(--f-serif);
	font-variation-settings: 'opsz' 18;
	font-size: var(--t-base);
	line-height: 1.6;
	color: var(--c-fg);
	margin: 0 0 var(--s-5);
	text-wrap: pretty;
}
.overview .prose p:last-child {
	margin-bottom: 0;
}
.overview .prose > p:first-of-type::first-letter {
	font-family: var(--f-display);
	font-variation-settings:
		'opsz' 144,
		'WONK' 1;
	font-weight: 600;
	font-size: 3.4em;
	line-height: 0.85;
	float: left;
	padding: 0.1em var(--s-3) 0 0;
	color: var(--c-accent);
	margin-top: 0.05em;
}

/* Guide-link row. Dotted hairline separator above; mono small-caps
   caption with a single accent link to /help. */
.guide-link-row {
	margin-top: var(--s-8);
	padding-top: var(--s-5);
	border-top: 1px dotted var(--c-border);
	display: flex;
	align-items: baseline;
	gap: var(--s-3);
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--c-fg-faint, var(--c-fg-dim));
}
.guide-link-row .glyph {
	color: var(--c-accent);
}
.guide-link {
	color: var(--c-accent);
	text-decoration: none;
	border-bottom: 1px solid transparent;
	letter-spacing: 0.18em;
	transition:
		color 120ms ease,
		border-color 120ms ease;
}
.guide-link:hover,
.guide-link:focus-visible {
	color: var(--c-accent-hover, var(--c-accent));
	border-bottom-color: currentColor;
}

/* Colophon footer. Matches /help and /about. Harper line one notch
   larger than Hardcover, as a quiet dogfooding nod. */
.signin-colophon {
	max-width: 32rem;
	width: 100%;
	margin: 0 auto;
	padding: var(--s-8) var(--s-6) var(--s-10);
	border-top: 1px solid var(--c-border-strong, var(--c-border));
	text-align: center;
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--c-fg-faint, var(--c-fg-dim));
	line-height: 1.9;
}
.signin-colophon .em {
	color: var(--c-fg-muted);
}
.signin-colophon a {
	color: var(--c-accent);
	text-decoration: none;
	border-bottom: 1px solid transparent;
}
.signin-colophon a:hover,
.signin-colophon a:focus-visible {
	border-bottom-color: currentColor;
	color: var(--c-accent-hover, var(--c-accent));
}
.signin-colophon .colo-line + .colo-line {
	margin-top: var(--s-1);
}
.signin-colophon .colo-line--feature {
	font-size: var(--t-sm);
	margin-top: var(--s-2);
}
.signin-colophon .closing-glyph {
	display: block;
	color: var(--c-accent);
	font-size: var(--t-md);
	margin-bottom: var(--s-3);
	font-family: var(--f-display);
	font-variation-settings: 'opsz' 60;
	letter-spacing: 0.4em;
	padding-left: 0.4em;
}

@media (max-width: 30rem) {
	.app.signed-out .chapbook-shell {
		padding: var(--s-10) var(--s-5) var(--s-6);
	}
	.signin-colophon {
		padding: var(--s-6) var(--s-5) var(--s-8);
	}
}

/* ──────────────── signed-in shell ──────────────── */

.app.signed-in {
	display: flex;
	flex-direction: column;
}

.app-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--s-4);
	padding: calc(var(--s-3) + env(safe-area-inset-top)) var(--s-5) var(--s-3);
	border-bottom: 1px solid var(--c-border);
}
.brand-mini h1 {
	font-family: var(--f-display);
	font-size: var(--t-lg);
	font-weight: 600;
	letter-spacing: -0.015em;
	font-variation-settings:
		'opsz' 48,
		'SOFT' 50,
		'WONK' 1;
}
.user-mini {
	display: flex;
	align-items: center;
	gap: var(--s-2);
	position: relative; /* anchors the absolutely-positioned account menu */
}
/* The user-mini trigger replaced the "sign out" link in commit <will-fill>.
   Avatar + small chevron together act as one button that toggles the
   account menu (Field guide / About / Sign out). Avatar styling unchanged;
   chevron is the only new chrome and it stays quiet. */
.avatar-img,
.avatar-fallback {
	width: 26px;
	height: 26px;
	border-radius: var(--r-2);
	border: 1px solid var(--c-border);
	background: var(--c-surface);
	display: flex;
	align-items: center;
	justify-content: center;
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	color: var(--c-fg-dim);
	opacity: 0.78;
	transition: opacity var(--motion-fast);
}
.user-mini:hover .avatar-img,
.user-mini:hover .avatar-fallback,
.user-menu-trigger[aria-expanded='true'] .avatar-img,
.user-menu-trigger[aria-expanded='true'] .avatar-fallback {
	opacity: 1;
}

.user-menu-trigger {
	display: flex;
	align-items: center;
	gap: var(--s-2);
	padding: var(--s-1) var(--s-2);
	margin: calc(-1 * var(--s-1)) calc(-1 * var(--s-2));
	background: transparent;
	border: 1px solid transparent;
	border-radius: var(--r-2);
	cursor: pointer;
	color: inherit;
	font: inherit;
	transition: border-color var(--motion-fast);
}
.user-menu-trigger:hover,
.user-menu-trigger:focus-visible,
.user-menu-trigger[aria-expanded='true'] {
	border-color: var(--c-border);
}
.user-menu-trigger:focus-visible {
	outline: none;
}
.user-menu-chevron {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	color: var(--c-fg-faint, var(--c-fg-dim));
	opacity: 0.7;
	transition:
		transform var(--motion-fast),
		opacity var(--motion-fast);
}
.user-menu-trigger[aria-expanded='true'] .user-menu-chevron {
	transform: rotate(180deg);
	opacity: 1;
}

/* ──── Account menu (the dropdown / popover itself) ────
   Variant B from the design exploration: chapbook panel with brass
   glyphs per item, dotted seam above sign-out, account/who header at
   the top. Anchored to .user-mini, slid out below + flush right. */
.account-menu {
	position: absolute;
	top: calc(100% + var(--s-2));
	right: 0;
	min-width: 13rem;
	padding: var(--s-2) 0;
	background: var(--c-surface);
	border: 1px solid var(--c-border);
	border-radius: var(--r-2);
	box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
	z-index: 200;
	font-family: var(--f-serif);
}
.account-menu[hidden] {
	display: none;
}
.account-menu-header {
	padding: var(--s-2) var(--s-4) var(--s-3);
	border-bottom: 1px solid var(--c-border);
	margin-bottom: var(--s-1);
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--c-fg-faint, var(--c-fg-dim));
	display: flex;
	align-items: baseline;
	gap: var(--s-2);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.account-menu-divider-mark {
	color: var(--c-border-strong, var(--c-border));
}
.account-menu-who {
	color: var(--c-fg-muted);
	text-transform: none;
	letter-spacing: 0;
	font-family: var(--f-serif);
	font-style: italic;
	font-size: var(--t-sm);
	overflow: hidden;
	text-overflow: ellipsis;
}
.account-menu-item {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	padding: var(--s-3) var(--s-4);
	color: var(--c-fg);
	font-family: var(--f-serif);
	font-size: var(--t-base);
	background: transparent;
	border: none;
	cursor: pointer;
	text-decoration: none;
	text-align: left;
	width: 100%;
	transition:
		background var(--motion-fast),
		color var(--motion-fast);
}
.account-menu-item:hover,
.account-menu-item:focus-visible {
	background: var(--c-surface-2, rgba(196, 160, 96, 0.08));
	color: var(--c-fg);
	outline: none;
}
.account-menu-glyph {
	color: var(--c-accent);
	font-size: var(--t-sm);
	min-width: 1rem;
	text-align: center;
}
.account-menu-text {
	flex: 1;
}
/* Dotted seam above sign-out. Marks "different register of action"
   per the design rationale (not just the next item in the list). */
.account-menu-seam {
	margin: var(--s-2) var(--s-3);
	border-top: 1px dotted var(--c-border-strong, var(--c-border));
}
.account-menu-signout {
	font-family: var(--f-mono);
	font-size: var(--t-sm);
	letter-spacing: 0.04em;
	color: var(--c-fg-muted);
}
.account-menu-signout:hover,
.account-menu-signout:focus-visible {
	color: var(--c-fg);
}

/* Mobile: switch from popover to bottom sheet. Avatar lives in the
   top-right corner which is the worst spot for one-thumb reach on a
   6-inch phone, so we dock the menu near where the thumb actually is.
   Same vocabulary, just rescaled to touch. */
@media (max-width: 30rem) {
	.account-menu {
		position: fixed;
		top: auto;
		right: 0;
		left: 0;
		bottom: 0;
		min-width: 0;
		width: 100%;
		max-height: 80vh;
		border-radius: var(--r-2) var(--r-2) 0 0;
		padding: var(--s-3) 0 calc(var(--s-6) + env(safe-area-inset-bottom));
		animation: account-menu-slide-up 180ms ease-out;
	}
	.account-menu-item {
		padding: var(--s-4) var(--s-5);
		font-size: var(--t-md);
	}
	.account-menu-header {
		padding: var(--s-3) var(--s-5) var(--s-4);
	}
}
@keyframes account-menu-slide-up {
	from {
		transform: translateY(100%);
	}
	to {
		transform: translateY(0);
	}
}
@media (prefers-reduced-motion: reduce) {
	.account-menu,
	.user-menu-chevron {
		animation: none;
		transition: none;
	}
}

/* `.app-shell` wraps the (optional desktop) sidebar + main content.
   On mobile + narrow desktop it's a flow container, sidebar is hidden;
   at >= 980px it switches to a grid so the rail + content sit side by
   side. The grid lives on the SHELL, not on .app-content, so the
   sidebar can carry its own width independent of --content-max. */
.app-shell {
	display: flex;
	flex-direction: column;
	width: 100%;
	flex: 1;
}

.app-content {
	flex: 1;
	width: 100%;
	max-width: var(--content-max);
	margin: 0 auto;
	padding: var(--s-6) var(--s-5);
	box-sizing: border-box;
}

/* Sidebar (desktop rail). Hidden below the breakpoint; populated by
   `mountSidebar()` with a compact followed-authors list. */
.app-sidebar {
	display: none;
}

/* View titles ("Library", "Settings", "3 new since…"). The middle tier
 * of the Fraunces hierarchy — display character at section scale, less
 * wonk than the wordmark, still has clear personality. Centered so the
 * page reads as a small masthead. */
.view-title {
	font-family: var(--f-display);
	font-size: var(--t-xl);
	font-weight: 500;
	font-variation-settings:
		'opsz' 36,
		'SOFT' 50,
		'WONK' 1;
	letter-spacing: -0.015em;
	margin-bottom: var(--s-4);
	text-align: center;
}
.view-title.view-title-contextual em {
	font-style: italic;
	color: var(--c-accent);
}

/* ──────────────── nav (tabs) ──────────────── */

.app-nav {
	display: flex;
	align-items: stretch;
	border-top: 1px solid var(--c-border);
	background: var(--c-bg);
}
.nav-tab {
	flex: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: var(--s-3) var(--s-2);
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--c-fg-dim);
	border: 0;
	border-bottom: 0;
	background: none;
	transition: color var(--motion-fast);
	border-top: 2px solid transparent; /* mobile active indicator */
}
.nav-tab:hover {
	color: var(--c-fg-muted);
}
.nav-tab.active {
	color: var(--c-accent);
}

/* ──────────────── content blocks (carry-over from Phase 3) ──────────────── */

.block {
	margin-bottom: var(--s-8);
}
.block-label {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--c-fg-dim);
	margin-bottom: var(--s-3);
}

.search-shell {
	position: relative;
}
#search-input {
	width: 100%;
	padding: var(--s-3) var(--s-4);
	background: var(--c-surface);
	border: 1px solid var(--c-border);
	color: var(--c-fg);
	font: inherit;
	border-radius: var(--r-2);
}
#search-input:focus {
	outline: none;
	border-color: var(--c-accent);
}
.search-dropdown {
	position: absolute;
	left: 0;
	right: 0;
	top: calc(100% + 4px);
	z-index: 10;
	background: var(--c-surface);
	border: 1px solid var(--c-border-strong);
	border-radius: var(--r-2);
	max-height: 60vh;
	overflow-y: auto;
	box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}
.search-row {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	padding: var(--s-3) var(--s-4);
	border-top: 1px solid var(--c-border);
	transition: background var(--motion-fast);
}
.search-row:first-child {
	border-top: 0;
}
/* Keyboard / mouse highlight on the dropdown rows (Phase 6.7
   combobox pattern). Soft brass tint matches the active-chip
   treatment — same visual vocabulary. */
.search-row.is-highlighted {
	background: rgba(196, 160, 96, 0.08);
}
.search-row-photo {
	width: 36px;
	height: 36px;
	border-radius: var(--r-2);
	border: 1px solid var(--c-border);
	object-fit: cover;
	background: var(--c-surface-2);
	flex-shrink: 0;
}
.search-row-photo.placeholder {
	background: var(--c-surface-2);
}
.search-row-main {
	flex: 1;
	min-width: 0;
}
.search-row-title {
	font-family: var(--f-display);
	font-size: var(--t-base);
	font-weight: 500;
	font-variation-settings:
		'opsz' 18,
		'SOFT' 50,
		'WONK' 0;
	letter-spacing: -0.005em;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.search-row-meta {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	color: var(--c-fg-dim);
}

.row {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	padding: var(--s-3) 0;
	border-top: 1px solid var(--c-border);
}
.row:last-child {
	border-bottom: 1px solid var(--c-border);
}
.row.push-row {
	justify-content: space-between;
}
.row-photo {
	width: 44px;
	height: 44px;
	border-radius: var(--r-2);
	border: 1px solid var(--c-border);
	object-fit: cover;
	background: var(--c-surface);
}
.row-photo.placeholder {
	background: var(--c-surface);
}
.row-main {
	flex: 1;
	min-width: 0;
}
.row-title {
	font-size: var(--t-md);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.row-meta {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	color: var(--c-fg-dim);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.row-actions {
	display: flex;
	gap: var(--s-3);
	align-items: center;
}

.button,
.button-google {
	display: inline-flex;
	align-items: center;
	gap: var(--s-2);
	padding: var(--s-2) var(--s-4);
	font-family: var(--f-mono);
	font-size: var(--t-sm);
	letter-spacing: 0.04em;
	color: var(--c-fg);
	background: transparent;
	border: 1px solid var(--c-border-strong);
	border-radius: var(--r-2);
	cursor: pointer;
	transition:
		border-color var(--motion-fast),
		color var(--motion-fast),
		background var(--motion-fast);
}
.button:hover,
.button:focus-visible,
.button-google:hover,
.button-google:focus-visible {
	border-color: var(--c-accent);
	color: var(--c-accent-hover);
}
.button:disabled {
	opacity: 0.5;
	cursor: default;
}

.linkish {
	color: var(--c-fg-dim);
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	text-transform: uppercase;
	letter-spacing: 0.08em;
	background: none;
	border: 0;
	padding: var(--s-2) var(--s-1); /* expands tap area without visual change */
	margin: calc(-1 * var(--s-2)) calc(-1 * var(--s-1)); /* keep layout footprint */
	cursor: pointer;
}
.linkish:hover {
	color: var(--c-accent);
}
.linkish.accent {
	color: var(--c-accent);
}
.linkish.accent:hover {
	color: var(--c-accent-hover);
}

.muted {
	color: var(--c-fg-dim);
	font-family: var(--f-mono);
	font-size: var(--t-sm);
}

/* Auto-save flag — slides in from the right of the block label, holds, fades */
.saved-flag {
	display: inline-block;
	margin-left: var(--s-2);
	color: var(--c-accent);
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.04em;
	text-transform: none;
	animation: saved-flag-pulse 2.4s ease-out forwards;
}
@keyframes saved-flag-pulse {
	0% {
		opacity: 0;
		transform: translateX(-4px);
	}
	15%,
	75% {
		opacity: 1;
		transform: translateX(0);
	}
	100% {
		opacity: 0;
		transform: translateX(0);
	}
}

/* follow rows on the Following view — these are now <a> elements linking
 * to the author detail page; the inline expand pattern lives only inside the
 * detail page now. */
.follow-row {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	padding: var(--s-3);
	margin: 0 calc(-1 * var(--s-3));
	border-top: 1px solid var(--c-border);
	border-radius: var(--r-2);
	color: inherit;
	text-decoration: none;
	transition:
		background var(--motion-fast),
		border-color var(--motion-fast);
}
.follow-row:hover,
.follow-row:focus-visible {
	background: var(--c-surface);
}
.follow-row:last-child {
	border-bottom: 1px solid var(--c-border);
}
.muted-flag {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--c-fg-dim);
	margin-left: var(--s-2);
}
.meta-tag {
	display: inline-block;
	font-size: var(--t-xs);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--c-fg-dim);
	min-width: 2.5rem;
}
.row-meta.accent .meta-tag {
	color: var(--c-accent);
}
.row-meta.accent {
	color: var(--c-fg);
}

/* legacy expandable books-pane — still used by the author detail page */
.follow-block {
	border-top: 1px solid var(--c-border);
}
.follow-block:last-child {
	border-bottom: 1px solid var(--c-border);
}
.follow-block .row {
	border-top: 0;
	border-bottom: 0;
}
.books-pane-content {
	padding: var(--s-3) 0 var(--s-5) 56px;
}
.books-pane-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	margin-bottom: var(--s-3);
	gap: var(--s-3);
	flex-wrap: wrap;
}
.books-pane-controls {
	display: flex;
	align-items: center;
	gap: var(--s-4);
}
.books-list {
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
}
.books-section + .books-section {
	margin-top: var(--s-4);
}
.books-section-label {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--c-fg-dim);
	margin-bottom: var(--s-2);
}
/* Phase 6.6 — book rows on Author detail.
 * State is encoded as a 3px left-edge bar (Vellum motif): oxblood for
 * upcoming, brass for is-read. Replaces the prior tag-upcoming chip +
 * row-dim treatment. The bar carries the signal so the title doesn't
 * have to fight a saturated pill for attention.
 */
.book-row {
	display: flex;
	align-items: flex-start;
	gap: var(--s-3);
	padding: var(--s-3);
	border: 1px solid var(--c-border);
	border-left: 3px solid transparent; /* reserved for state bar */
	padding-left: calc(var(--s-3) - 2px); /* compensate for the 3px border */
	border-radius: var(--r-2);
	background: var(--c-surface);
	cursor: pointer;
	transition:
		border-color var(--motion-fast),
		background var(--motion-fast);
}
.book-row:hover,
.book-row:focus-within {
	border-color: var(--c-border-strong);
	background: var(--c-surface-2);
}
.book-row.is-read {
	border-left-color: var(--c-accent);
}
.book-row.is-read .book-title {
	color: var(--c-fg-muted);
}
.book-row.is-upcoming {
	border-left-color: var(--c-oxblood);
}
.book-cover {
	width: 40px;
	height: 56px;
	object-fit: cover;
	border-radius: var(--r-1);
	border: 1px solid var(--c-border);
	background: var(--c-surface-2);
	/* The icon's tooled-border feel: a 1px brass inner stroke on every
	 * cover unifies light-edged and dark-edged covers against the dark
	 * ground. Polish-agent finding (Phase 6.1). */
	box-shadow: inset 0 0 0 1px rgba(196, 160, 96, 0.18);
	flex-shrink: 0;
}
.book-cover.placeholder {
	background: var(--c-surface-2);
}
.book-main {
	flex: 1;
	min-width: 0;
}
/* Book titles share Fraunces at the quietest tier — WONK 0 calms the
 * hand-cut quirk for long lists. Optical-size axis still earns its
 * keep at body scale. Identical setting on .release-title and
 * .library-title so dense lists feel cohesive. */
.book-title {
	font-family: var(--f-display);
	font-size: var(--t-base);
	font-weight: 500;
	font-variation-settings:
		'opsz' 18,
		'SOFT' 50,
		'WONK' 0;
	letter-spacing: -0.005em;
	line-height: 1.3;
	overflow: hidden;
	text-overflow: ellipsis;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
}
.book-meta {
	display: flex;
	gap: var(--s-3);
	align-items: center;
	margin-top: var(--s-1);
	color: var(--c-fg-dim);
	font-size: var(--t-xs);
}
/* `tag-upcoming` retired from book rows in Phase 6.6 — replaced by the
 * 3px left-edge oxblood bar on `.book-row.is-upcoming` /
 * `.release-row.upcoming`. The chip survives only inside the book
 * modal where it sits alongside the date in the meta line as the sole
 * upcoming signal. Restyled here in the new oxblood-tinted treatment. */
.tag-upcoming {
	font-family: var(--f-serif);
	font-variant-caps: all-small-caps;
	letter-spacing: 0.1em;
	font-size: var(--t-sm);
	color: var(--c-oxblood-hover);
	padding: 0 var(--s-2);
	border-left: 2px solid var(--c-oxblood);
}
.book-date {
	font-family: var(--f-serif);
	font-variant-caps: all-small-caps;
	letter-spacing: 0.08em;
	font-size: var(--t-sm);
	color: var(--c-accent);
}
.upcoming-pill {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--c-fg-dim);
	white-space: nowrap;
}

.stars {
	display: inline-flex;
	gap: 2px;
	/* When the .stars container itself is the slider (Phase 6.7 a11y),
	   it needs a tap target + visible focus ring. Inner stars become
	   aria-hidden decoration. */
	padding: 2px;
	border-radius: var(--r-1);
}
.stars[role='slider']:focus-visible {
	outline: 2px solid var(--c-accent);
	outline-offset: 1px;
}
.stars[aria-disabled='true'] {
	opacity: 0.55;
}
.star {
	background: none;
	border: 0;
	padding: 0;
	font-size: 14px;
	line-height: 1;
	color: var(--c-fg-dim);
	cursor: pointer;
	transition: color var(--motion-fast);
}
.star.filled {
	color: var(--c-accent);
}
.star.half {
	background: linear-gradient(to right, var(--c-accent) 50%, var(--c-fg-dim) 50%);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
}
.book-row.is-upcoming .star {
	cursor: default;
}
.book-row.is-upcoming .star:hover {
	color: var(--c-fg-dim);
}

/* ──────────────── push banner ──────────────── */

#push-banner {
	max-width: var(--content-max);
	margin: 0 auto;
	padding: 0 var(--s-5);
	width: 100%;
	box-sizing: border-box;
}
#push-banner[hidden] {
	display: none;
}
.push-banner-inner {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	padding: var(--s-3) var(--s-4);
	margin-top: var(--s-3);
	background: var(--c-surface);
	border: 1px solid var(--c-accent-dim);
	border-radius: var(--r-2);
	font-size: var(--t-sm);
	color: var(--c-fg);
	flex-wrap: wrap;
}
.push-banner-inner > :first-child {
	flex: 1;
	min-width: 12rem;
}

/* ──────────────── info hint ⓘ + tooltip ──────────────── */

.info-hint {
	/* Phase 6.7: now a real <button>. Reset the UA defaults so it
	   keeps the inline-glyph look the prior <span> had. */
	background: transparent;
	border: 0;
	padding: 0;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 16px;
	height: 16px;
	margin-left: var(--s-1);
	border-radius: 50%;
	font-family: var(--f-mono);
	font-size: 10px;
	color: var(--c-fg-dim);
	cursor: help;
	transition: color var(--motion-fast);
	vertical-align: 1px;
}
.info-hint:hover,
.info-hint:focus-visible {
	color: var(--c-accent);
	outline: none;
}

/* Floating tooltip used by `web/components/tooltip.js`. Position is set
 * inline by the JS. */
.tooltip {
	position: fixed;
	max-width: 20rem;
	padding: var(--s-2) var(--s-3);
	background: var(--c-surface-2);
	border: 1px solid var(--c-border-strong);
	border-radius: var(--r-2);
	color: var(--c-fg);
	font-family: var(--f-serif);
	font-size: var(--t-sm);
	line-height: var(--lh-snug);
	z-index: 200;
	pointer-events: none;
	box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
	animation: tooltip-fade-in var(--motion-fast) ease forwards;
}
.tooltip[hidden] {
	display: none;
}
@keyframes tooltip-fade-in {
	from {
		opacity: 0;
		transform: translateY(2px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}

/* ──────────────── view header + chips ──────────────── */

/* .view-header was the wrapper for the (now-removed) Releases
 * "edit defaults" chip. The visible-title cut means Releases either
 * shows its content-bearing H1 alone (no chip) or no header at all.
 * Library / Settings / Authors use sr-only H2s. .view-header no
 * longer applies anywhere; the rule is intentionally absent. */
.chips-row {
	display: flex;
	align-items: center;
	gap: var(--s-2);
	margin-bottom: var(--s-3);
	flex-wrap: wrap;
}
.chips-row.inline {
	margin-bottom: 0;
}
.language-chips .chip {
	font-family: var(--f-serif);
	font-size: var(--t-sm);
	letter-spacing: 0;
}
.setting-row-stacked {
	flex-direction: column;
	align-items: flex-start;
	gap: var(--s-2);
}
.setting-row-stacked .setting-label {
	min-width: 0;
}
.chips-label {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--c-fg-dim);
	margin-right: var(--s-2);
}
.chip {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.04em;
	padding: var(--s-2) var(--s-3);
	background: transparent;
	border: 1px solid var(--c-border);
	border-radius: var(--r-2);
	color: var(--c-fg-muted);
	cursor: pointer;
	transition:
		border-color var(--motion-fast),
		color var(--motion-fast),
		background var(--motion-fast);
}
.chip:hover {
	border-color: var(--c-border-strong);
	color: var(--c-fg);
}
/* Active chip — soft brass-tinted fill instead of a bare brass outline.
 * Phase 6.6: makes the selection state read more confidently against
 * the warm-dark ground (the prior outline-only treatment got lost on
 * mobile). Border stays brass to anchor; fill is a translucent brass
 * so the chip carries weight without going full chip-as-pill. */
.chip.active {
	border-color: var(--c-accent);
	color: var(--c-accent-hover);
	background: rgba(196, 160, 96, 0.08);
}
.chip.active:hover {
	background: rgba(196, 160, 96, 0.12);
}

.empty-state {
	padding: var(--s-6) var(--s-5);
	border: 1px solid var(--c-border);
	border-radius: var(--r-2);
	background: var(--c-surface);
}
.empty-state p {
	margin-bottom: var(--s-3);
}
.empty-state p:last-child {
	margin-bottom: 0;
}

/* ──────────────── settings ──────────────── */

.setting-row {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	margin-bottom: var(--s-3);
	flex-wrap: wrap;
}
.setting-label {
	min-width: 12rem;
	font-size: var(--t-base);
	color: var(--c-fg);
}
.setting-input {
	width: 6rem;
	padding: var(--s-2) var(--s-3);
	background: var(--c-surface);
	border: 1px solid var(--c-border);
	color: var(--c-fg);
	font-family: var(--f-mono);
	font-size: var(--t-base);
	border-radius: var(--r-2);
}
.setting-input:focus {
	outline: none;
	border-color: var(--c-accent);
}
.setting-input.narrow {
	width: 4.5rem;
	padding: var(--s-1) var(--s-2);
	font-size: var(--t-sm);
}
.setting-hint {
	color: var(--c-fg-dim);
	font-size: var(--t-xs);
}
.setting-actions {
	display: flex;
	align-items: center;
	gap: var(--s-4);
	margin-top: var(--s-3);
}

.per-author-table {
	display: flex;
	flex-direction: column;
}
.per-author-row {
	display: grid;
	grid-template-columns: 1fr auto auto;
	gap: var(--s-3);
	align-items: center;
	padding: var(--s-3) 0;
	border-top: 1px solid var(--c-border);
}
.per-author-row:last-child {
	border-bottom: 1px solid var(--c-border);
}
.per-author-name {
	color: inherit;
	text-decoration: none;
	font-size: var(--t-base);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.per-author-name:hover {
	color: var(--c-accent);
}
.per-author-cell {
	display: flex;
	align-items: center;
	gap: var(--s-2);
}

.toggle-row .toggle-button {
	min-width: 4rem;
}

@media (max-width: 480px) {
	.per-author-row {
		grid-template-columns: 1fr;
		gap: var(--s-2);
	}
}

/* ──────────────── releases ──────────────── */

.release-section {
	margin-bottom: var(--s-6);
}
.release-section:last-child {
	margin-bottom: 0;
}
.release-empty {
	padding: var(--s-2) 0;
}
.releases-list {
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
}
.release-row {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	padding: var(--s-3);
	border: 1px solid var(--c-border);
	border-left: 3px solid transparent; /* reserved for state bar */
	padding-left: calc(var(--s-3) - 2px);
	border-radius: var(--r-2);
	background: var(--c-surface);
	color: inherit;
	text-decoration: none;
	transition:
		border-color var(--motion-fast),
		background var(--motion-fast);
}
.release-row:hover,
.release-row:focus-visible {
	border-color: var(--c-border-strong);
	background: var(--c-surface-2);
}
.release-row.upcoming {
	border-left-color: var(--c-oxblood);
}
.release-row.is-read {
	border-left-color: var(--c-accent);
}
.release-row.is-read .release-title {
	color: var(--c-fg-muted);
}

/* Asterisk indicator (footnote-style) surfaces on any row whose
 * ReadEntry has a notes value. Serif glyph, brass-tinted, sized
 * slightly larger than its neighbors so the eye catches it without
 * the row feeling busy. Translated down 1px so the asterisk's
 * optical baseline matches the small-caps date / italic author. */
.has-notes-marker {
	font-family: var(--f-serif);
	font-size: 18px;
	line-height: 1;
	color: var(--c-accent);
	display: inline-block;
	transform: translateY(2px);
}

/* Recent-section "hide read" / "show N read" toggle, displayed inline
 * with the section label. Small bordered chip-style affordance. */
.release-section-label {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--s-3);
}
.release-section-toggle {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.06em;
}
.release-cover {
	width: 44px;
	height: 60px;
	object-fit: cover;
	border-radius: var(--r-1);
	border: 1px solid var(--c-border);
	background: var(--c-surface-2);
	box-shadow: inset 0 0 0 1px rgba(196, 160, 96, 0.18);
	flex-shrink: 0;
}
.release-cover.placeholder {
	background: var(--c-surface-2);
}
.release-main {
	flex: 1;
	min-width: 0;
}
.release-title {
	font-family: var(--f-display);
	font-size: var(--t-md);
	font-weight: 500;
	font-variation-settings:
		'opsz' 20,
		'SOFT' 50,
		'WONK' 0;
	letter-spacing: -0.005em;
	line-height: 1.3;
	overflow: hidden;
	text-overflow: ellipsis;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
}
.release-meta {
	display: flex;
	gap: var(--s-3);
	align-items: baseline;
	margin-top: var(--s-1);
	font-size: var(--t-xs);
	color: var(--c-fg-dim);
}
.release-author {
	font-family: var(--f-serif);
	font-style: italic;
	font-size: var(--t-sm);
	color: var(--c-fg-muted);
}
.release-date {
	font-family: var(--f-serif);
	font-variant-caps: all-small-caps;
	letter-spacing: 0.08em;
	font-size: var(--t-sm);
	color: var(--c-accent);
}
.release-row.upcoming .release-date {
	color: var(--c-accent-hover);
}

/* ──────────────── author detail ──────────────── */

.back-link {
	display: inline-block;
	margin-bottom: var(--s-4);
}

/* Author detail header card. Phase 6.3b restructure: the previous
   pipe-separated controls toolbar ("notifications on · unfollow")
   read as whispered, and the per-author lead-time used to live in
   Settings → per-author overrides (now deleted). The card collocates
   photo + name + bio + notify toggle + lead-time + unfollow in one
   bordered surface. */
.author-header-card {
	display: flex;
	flex-direction: column;
	gap: var(--s-4);
	padding: var(--s-5);
	margin-bottom: var(--s-6);
	background: var(--c-surface);
	border: 1px solid var(--c-border);
	border-radius: var(--r-2);
}
.author-header {
	display: flex;
	align-items: center;
	gap: var(--s-4);
}
.author-photo {
	width: 72px;
	height: 72px;
	border-radius: var(--r-2);
	border: 1px solid var(--c-border);
	box-shadow: inset 0 0 0 1px rgba(196, 160, 96, 0.18);
	object-fit: cover;
	background: var(--c-surface-2);
	flex-shrink: 0;
}
.author-photo.placeholder {
	background: var(--c-surface-2);
}
/* Entity identity — Fraunces italic at lower wonk than the wordmark
 * + view title. Reads as "this is a particular thing" without going
 * full editorial-display. */
.author-name {
	font-family: var(--f-display);
	font-style: italic;
	font-size: var(--t-xl);
	font-weight: 500;
	font-variation-settings:
		'opsz' 30,
		'SOFT' 50,
		'WONK' 0.5;
	letter-spacing: -0.015em;
}
.author-meta {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--c-fg-dim);
	margin-top: var(--s-1);
}
.author-bio {
	color: var(--c-fg-muted);
	font-size: var(--t-base);
	line-height: var(--lh-body);
	padding-top: var(--s-3);
	border-top: 1px solid var(--c-border);
	margin: 0;
	white-space: pre-line; /* Hardcover bios often carry paragraph breaks */
}
.author-bio.collapsed {
	display: -webkit-box;
	-webkit-line-clamp: 4;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.author-bio-more {
	align-self: flex-start;
	margin-top: calc(var(--s-2) * -1);
}
.author-control-grid {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: var(--s-4);
	padding-top: var(--s-3);
	border-top: 1px solid var(--c-border);
}
.author-control {
	display: flex;
	flex-direction: column;
	gap: var(--s-1);
}
.author-control-label {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--c-fg-dim);
}
.author-control-value {
	display: flex;
	align-items: baseline;
	gap: var(--s-2);
}
.author-control-value .notify-toggle {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--c-accent);
	background: none;
	border: 0;
	padding: 0;
	cursor: pointer;
	text-align: left;
}
.author-control-value .notify-toggle.muted {
	color: var(--c-fg-dim);
}
.author-control-value .lead-input {
	width: 56px;
	padding: var(--s-1) var(--s-2);
	background: var(--c-bg);
	border: 1px solid var(--c-border);
	border-radius: var(--r-1);
	color: var(--c-fg);
	font-family: var(--f-mono);
	font-size: var(--t-sm);
	text-align: center;
}
.author-control-value .lead-input:focus {
	outline: none;
	border-color: var(--c-accent);
}
.author-control-value .lead-unit {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	color: var(--c-fg-dim);
}
.author-control-value .lead-default-hint {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	color: var(--c-fg-dim);
	font-style: italic;
}
/* Bibliography filter input — shows up only when an author has > 8
 * books, to avoid noise on authors where you can scan the whole list. */
.books-search-input {
	width: 100%;
	padding: var(--s-2) var(--s-3);
	margin-bottom: var(--s-4);
	background: var(--c-surface);
	border: 1px solid var(--c-border);
	border-radius: var(--r-1);
	color: var(--c-fg);
	font: inherit;
	font-size: var(--t-sm);
}
.books-search-input:focus {
	outline: none;
	border-color: var(--c-accent);
}
.books-search-input::placeholder {
	color: var(--c-fg-dim);
	font-style: italic;
}

.author-unfollow-link {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--c-fg-dim);
	background: none;
	border: 0;
	padding: 0;
	text-align: left;
	cursor: pointer;
	align-self: flex-start;
	opacity: 0.75;
	transition:
		opacity var(--motion-fast),
		color var(--motion-fast);
}
.author-unfollow-link:hover {
	opacity: 1;
	color: var(--c-fg-muted);
}

/* Lingering reference: the old .author-controls block still appears
   in the not-following CTA fragment. Keep it minimal so the follow
   button has breathing room. */
.author-controls {
	display: flex;
	align-items: center;
	gap: var(--s-4);
	margin-bottom: var(--s-4);
}

/* Settings → Languages disclosure (Phase 6.3b). The full chip cloud
   used to burn 20% of the page vertical and visually compete with the
   bottom-nav state. Now collapsed by default. */
.disclosure-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--s-3);
	padding: var(--s-3) 0;
	cursor: pointer;
	background: none;
	border: 0;
	width: 100%;
	text-align: left;
	color: var(--c-fg);
	font: inherit;
}
.disclosure-row .disclosure-caret {
	font-family: var(--f-mono);
	color: var(--c-fg-dim);
	transition: transform var(--motion-fast);
}
.disclosure-row[aria-expanded='true'] .disclosure-caret {
	transform: rotate(90deg);
}
.disclosure-row .selected-count {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	color: var(--c-fg-dim);
	margin-left: var(--s-2);
}
.disclosure-content {
	padding-top: var(--s-3);
}

/* Per-author overrides moved to the Author detail page (Phase 6.3b).
   The Settings section is now a single redirect line. */
.overrides-redirect {
	color: var(--c-fg-muted);
	font-size: var(--t-sm);
	line-height: var(--lh-body);
}
.overrides-redirect a {
	color: var(--c-accent);
}

/* ──────── hidden recommendations (Phase 2 step 4 slice C) ─────────
   Settings → "hidden recommendations" — each row shows a small cover,
   title, author, when-dismissed, the scope ("hid this" vs "hid this &
   similar"), and a "show again" link. Restrained: no card chrome, just
   hairline separators between rows, same density as the per-author
   bibliography rows elsewhere. */

.dismissals-intro {
	font-size: var(--t-sm);
	line-height: var(--lh-body);
	margin: 0 0 var(--s-3);
}
.dismissals-intro em {
	font-style: normal;
	color: var(--c-fg);
}
.dismissals-empty {
	font-size: var(--t-sm);
	line-height: var(--lh-body);
	margin: 0;
}
.dismissals-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
}
.dismissal-row {
	display: grid;
	grid-template-columns: auto 1fr auto;
	align-items: center;
	gap: var(--s-3);
	padding: var(--s-3) 0;
	border-top: 1px solid var(--c-border-soft, var(--c-border));
}
.dismissal-row:first-child {
	border-top: none;
}
.dismissal-cover {
	width: 34px;
	height: 50px;
	object-fit: cover;
	border-radius: 2px;
	background: var(--c-bg-soft, transparent);
}
.dismissal-cover-placeholder {
	border: 1px dashed var(--c-border);
}
.dismissal-meta {
	min-width: 0;
}
.dismissal-title {
	font-size: var(--t-sm);
	line-height: var(--lh-tight);
	color: var(--c-fg);
	overflow: hidden;
	text-overflow: ellipsis;
}
.dismissal-author {
	font-size: var(--t-xs);
	line-height: var(--lh-tight);
	margin-top: 2px;
}
.dismissal-sub {
	font-size: var(--t-xs);
	margin-top: 4px;
	display: flex;
	align-items: center;
	gap: var(--s-1);
	flex-wrap: wrap;
}
.dismissal-scope-badge {
	font-family: var(--f-mono);
	font-size: 10px;
	letter-spacing: 0.04em;
	text-transform: lowercase;
	padding: 1px 6px;
	border: 1px solid var(--c-border);
	border-radius: 999px;
	color: var(--c-fg-muted);
	background: transparent;
}
.dismissal-scope-books-like-this {
	/* The stronger signal — give it a touch more weight without going
	   garish. Same restraint as the modal's two-tier dismiss group. */
	color: var(--c-fg);
	border-color: var(--c-fg-muted);
}
.dismissal-when {
	color: var(--c-fg-muted);
}
.dismissal-restore {
	font-size: var(--t-xs);
	white-space: nowrap;
	padding: var(--s-1) var(--s-2);
}

/* ──────────────── library sub-tabs (TBR / Read) ──────────────── */

.library-subtabs {
	display: flex;
	justify-content: center;
	gap: var(--s-2);
	margin-bottom: var(--s-6);
}
.library-subtab {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	padding: var(--s-2) var(--s-4);
	background: transparent;
	border: 1px solid var(--c-border);
	border-radius: var(--r-1);
	color: var(--c-fg-dim);
	cursor: pointer;
	transition:
		border-color var(--motion-fast),
		color var(--motion-fast),
		background var(--motion-fast);
}
.library-subtab:hover {
	border-color: var(--c-border-strong);
	color: var(--c-fg);
}
.library-subtab.active {
	border-color: var(--c-accent);
	color: var(--c-accent-hover);
	background: rgba(196, 160, 96, 0.08);
}

/* ──────────────── tbr ──────────────── */

.tbr-search-shell {
	position: relative;
	margin-bottom: var(--s-5);
}
.tbr-search-input {
	width: 100%;
	padding: var(--s-3) var(--s-4);
	background: var(--c-surface);
	border: 1px solid var(--c-border);
	color: var(--c-fg);
	font: inherit;
	border-radius: var(--r-2);
}
.tbr-search-input:focus {
	outline: none;
	border-color: var(--c-accent);
}
.tbr-search-input::placeholder {
	color: var(--c-fg-dim);
	font-style: italic;
}
.tbr-search-results {
	margin-top: var(--s-2);
	background: var(--c-surface);
	border: 1px solid var(--c-border);
	border-radius: var(--r-2);
	overflow: hidden;
}
.tbr-search-row .search-row-meta {
	font-family: var(--f-serif);
	font-style: italic;
	font-size: var(--t-sm);
	color: var(--c-fg-muted);
}
.tbr-search-row .muted {
	font-style: normal;
}

.tbr-empty {
	padding: var(--s-6) 0;
	text-align: center;
}

/* State markers — share size + transform with .has-notes-marker (✱)
 * so all three glyphs align optically on the meta line. */
.tbr-marker,
.reading-marker {
	font-family: var(--f-serif);
	font-size: 18px;
	line-height: 1;
	color: var(--c-accent);
	display: inline-block;
	transform: translateY(2px);
	background: transparent;
	border: 0;
	padding: 0;
}
/* The .info-hint base rule adds button-reset + hover affordance; these
 * markers are buttons-as-glyphs so they pick that up too. The cursor
 * stays `help` per .info-hint. */

/* TBR row variant of library-row: adds a quiet "↑ top" button at the
 * right edge of every row except the first (which is already top). */
.tbr-row .move-to-top-button {
	flex-shrink: 0;
	font-size: var(--t-xs);
}

/* Inline "+ shelf" button surfaces on book rows whose status is NOT
 * already tbr/reading. Mono caps brass on hover; resting state is the
 * quieter dim. Stays compact so it doesn't dominate the row. */
.add-to-shelf-button {
	flex-shrink: 0;
	font-size: var(--t-xs);
	white-space: nowrap;
}

/* Shelf sub-tab section labels — extra top-margin between sections
 * since each contains its own list. */
.shelf-section + .shelf-section {
	margin-top: var(--s-6);
}

/* ──────────────── library ──────────────── */

/* Stat strip: four equal-width badges that share the viewport. Grid
 * gives each cell 1fr so the row never wraps — content shrinks via
 * clamp() on padding + font-size as viewport narrows. */
.library-summary {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: clamp(0.25rem, 1.5vw, var(--s-3));
	margin-bottom: var(--s-6);
}
.library-stat {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: var(--s-1);
	padding: clamp(var(--s-2), 2vw, var(--s-3)) clamp(var(--s-1), 1.5vw, var(--s-4));
	min-width: 0;
	background: var(--c-surface);
	border: 1px solid var(--c-border);
	border-radius: var(--r-2);
}
.library-stat-num {
	font-family: var(--f-display);
	font-size: clamp(1rem, 4.5vw, var(--t-xl));
	font-weight: 600;
	font-variation-settings:
		'opsz' 36,
		'SOFT' 50,
		'WONK' 0.5;
	line-height: 1;
	color: var(--c-accent);
}
.library-stat-label {
	font-family: var(--f-mono);
	font-size: clamp(0.625rem, 1.8vw, var(--t-xs));
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--c-fg-dim);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	max-width: 100%;
}

.library-list {
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
}
.library-row {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	padding: var(--s-3);
	border: 1px solid var(--c-border);
	border-left: 3px solid var(--c-accent); /* every library row is is-read */
	padding-left: calc(var(--s-3) - 2px);
	border-radius: var(--r-2);
	background: var(--c-surface);
	color: inherit;
	text-decoration: none;
	transition:
		border-color var(--motion-fast),
		background var(--motion-fast);
}
.library-row:hover,
.library-row:focus-visible {
	border-color: var(--c-border-strong);
	background: var(--c-surface-2);
}
.library-cover {
	width: 40px;
	height: 56px;
	object-fit: cover;
	border-radius: var(--r-1);
	border: 1px solid var(--c-border);
	background: var(--c-surface-2);
	box-shadow: inset 0 0 0 1px rgba(196, 160, 96, 0.18);
	flex-shrink: 0;
}
.library-cover.placeholder {
	background: var(--c-surface-2);
}
.library-main {
	flex: 1;
	min-width: 0;
}
.library-title {
	font-family: var(--f-display);
	font-size: var(--t-base);
	font-weight: 500;
	font-variation-settings:
		'opsz' 18,
		'SOFT' 50,
		'WONK' 0;
	letter-spacing: -0.005em;
	line-height: 1.3;
	overflow: hidden;
	text-overflow: ellipsis;
	display: -webkit-box;
	-webkit-line-clamp: 1;
	-webkit-box-orient: vertical;
}
.library-meta {
	display: flex;
	gap: var(--s-3);
	margin-top: var(--s-1);
	font-size: var(--t-xs);
	color: var(--c-fg-dim);
}
.library-author {
	font-family: var(--f-serif);
	font-style: italic;
	font-size: var(--t-sm);
	color: var(--c-fg-muted);
}
.library-date {
	font-family: var(--f-serif);
	font-variant-caps: all-small-caps;
	letter-spacing: 0.08em;
	font-size: var(--t-sm);
	color: var(--c-accent);
}
.library-row .stars {
	flex-shrink: 0;
}

/* ─── cover-forward recs rows (Phase 2 step 8) ───
   Recs rows reuse the .library-row element but want a different feel: the
   cover leads, no boxed card, no is-read brass left-bar; entries separated
   by a hairline. All scoped to .recs-row so the library list is untouched. */
.recs-list {
	display: flex;
	flex-direction: column;
}
.recs-row {
	align-items: flex-start;
	gap: var(--s-4);
	padding: var(--s-5) var(--s-1);
	border: 0;
	border-bottom: 1px solid var(--c-border);
	border-radius: 0;
	background: transparent;
}
.recs-row:last-child {
	border-bottom: 0;
}
.recs-row:hover,
.recs-row:focus-visible {
	border-color: var(--c-border);
	background: transparent;
}
.recs-row .library-cover {
	width: 76px;
	height: 114px;
	border-radius: 0 var(--r-2) var(--r-2) 0;
	border-left: 3px solid var(--c-accent-dim);
	box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
}
.recs-row:hover .library-cover {
	border-left-color: var(--c-accent);
}
.recs-row .library-main {
	padding-top: var(--s-1);
}
.recs-row .library-title {
	font-family: var(--f-serif);
	font-size: var(--t-md);
	font-variation-settings: 'opsz' 22;
	letter-spacing: -0.005em;
	-webkit-line-clamp: 2;
	line-height: 1.25;
}
.recs-row .recs-because {
	color: var(--c-fg-faint);
}

/* ──────────────── modal ──────────────── */

#modal-root {
	position: fixed;
	inset: 0;
	z-index: 100;
	pointer-events: none;
}
#modal-root.open {
	pointer-events: auto;
}
.modal-backdrop {
	position: absolute;
	inset: 0;
	background: rgba(8, 9, 10, 0.6);
	backdrop-filter: blur(4px);
	animation: modal-fade-in var(--motion-fast) forwards;
}
.modal {
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	width: min(28rem, calc(100vw - var(--s-6)));
	max-height: calc(100vh - var(--s-6));
	max-height: calc(100dvh - var(--s-6));
	overflow-y: auto;
	background: var(--c-surface);
	border: 1px solid var(--c-border-strong);
	border-radius: var(--r-2);
	padding: var(--s-6);
	box-shadow: 0 16px 48px rgba(0, 0, 0, 0.6);
	animation: modal-rise var(--motion-slow) forwards;
}
.modal-title {
	font-family: var(--f-display);
	font-size: var(--t-lg);
	font-weight: 500;
	font-variation-settings:
		'opsz' 24,
		'SOFT' 50,
		'WONK' 0.5;
	letter-spacing: -0.01em;
	margin-bottom: var(--s-3);
}
.modal-body {
	color: var(--c-fg-muted);
	font-size: var(--t-base);
	line-height: var(--lh-body);
	margin-bottom: var(--s-5);
}
.modal-actions {
	display: flex;
	align-items: center;
	justify-content: flex-end;
	gap: var(--s-4);
}
/* The wide (book-detail) modal uses its own inner scroll so the close
 * button can stay pinned at the top-right while the content body scrolls.
 * Without this, long book descriptions push the actions off-screen. */
.modal-wide {
	width: min(32rem, calc(100vw - var(--s-6)));
	padding: 0;
	display: flex;
	flex-direction: column;
	overflow: hidden; /* override .modal's overflow-y so the inner content scrolls instead */
}
.modal-wide > .modal-content {
	flex: 1;
	min-height: 0;
	overflow-y: auto;
	padding: var(--s-6) var(--s-6) var(--s-5);
}
.modal-close {
	position: absolute;
	top: var(--s-2);
	right: var(--s-2);
	width: 44px;
	height: 44px;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 20px;
	color: var(--c-fg-dim);
	background: transparent;
	border: 0;
	cursor: pointer;
	border-radius: var(--r-2);
}
.modal-close:hover {
	color: var(--c-fg);
	background: var(--c-surface-2);
}

/* ──────────────── book detail modal ──────────────── */

.book-modal-head {
	display: flex;
	gap: var(--s-4);
	margin-bottom: var(--s-5);
	align-items: flex-start;
}
.book-modal-cover {
	width: 64px;
	height: 92px;
	flex-shrink: 0;
	object-fit: cover;
	border-radius: var(--r-1);
	border: 1px solid var(--c-border);
	box-shadow: inset 0 0 0 1px rgba(196, 160, 96, 0.18);
	background: var(--c-surface-2);
}
.book-modal-cover.placeholder {
	background: var(--c-surface-2);
}
.book-modal-id {
	flex: 1;
	min-width: 0;
}
.book-modal-title {
	font-family: var(--f-display);
	font-size: var(--t-lg);
	font-weight: 500;
	font-variation-settings:
		'opsz' 24,
		'SOFT' 50,
		'WONK' 0.5;
	letter-spacing: -0.01em;
	margin-bottom: var(--s-1);
	line-height: var(--lh-snug);
}
.book-modal-author {
	display: inline-block;
	font-family: var(--f-serif);
	font-size: var(--t-base);
	color: var(--c-accent);
	text-decoration: none;
	margin-bottom: var(--s-2);
}
.book-modal-author:hover {
	color: var(--c-accent-hover);
	text-decoration: underline;
}
.book-modal-meta {
	display: flex;
	gap: var(--s-3);
	align-items: center;
	font-size: var(--t-xs);
	color: var(--c-fg-dim);
}
.book-modal-note {
	margin-bottom: var(--s-3);
}
/* Quick-mark-read affordance for unread non-upcoming books in the modal.
   Earlier impl wrapped a primary button in a bordered-accent card, which
   read like an alert and conflicted with "you can adjust below" (since
   it actually saved + closed). New behavior: same pattern as clicking a
   star — sets local state, rerenders, modal stays open. So the visual
   needs to be a quiet inline affordance, not a primary CTA. */
/* Status display + commit-on-tap action buttons. Replaces both the
   old "mark as read" CTA and the status <select>. Matches the rest of
   the modal's form-field grid: label on the left, content stacked on
   the right. align-items: flex-start overrides the row-default center
   alignment so the label sits at the top of the stacked body. */
.book-modal-status-field {
	align-items: flex-start;
}
.book-modal-status-body {
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
	flex: 1;
	min-width: 0;
}
.book-modal-status-line {
	display: flex;
	align-items: baseline;
	gap: var(--s-2);
	flex-wrap: wrap;
}
.book-modal-status-current {
	color: var(--c-fg);
	font-weight: 600;
}
.book-modal-duration {
	color: var(--c-fg-muted);
	font-size: var(--t-sm, 0.875rem);
	font-style: italic;
}
.book-modal-status-actions {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--s-2);
}
.status-action-button {
	margin: 0;
	padding: var(--s-2) var(--s-3);
	background: transparent;
	color: var(--c-fg);
	font: inherit;
	border: 1px solid var(--c-border-strong, var(--c-border));
	border-radius: var(--r-1);
	cursor: pointer;
	transition:
		border-color var(--motion-fast),
		color var(--motion-fast);
}
.status-action-button:hover {
	border-color: var(--c-accent);
	color: var(--c-accent);
}

/* Reading-section row gets the live "Reading for X" line under the
   author/date meta. Muted + small so it reads as a quiet annotation
   rather than competing with the title. */
.library-duration {
	color: var(--c-fg-muted);
	font-size: var(--t-sm, 0.875rem);
	font-style: italic;
	margin-top: var(--s-1);
}
.book-modal-description {
	color: var(--c-fg-muted);
	font-size: var(--t-base);
	line-height: var(--lh-body);
	margin-bottom: var(--s-2);
	white-space: pre-line; /* Hardcover descriptions often have paragraph breaks */
}
.book-modal-description.collapsed {
	display: -webkit-box;
	-webkit-line-clamp: 4;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.book-modal-more {
	margin-bottom: var(--s-4);
}
.book-modal-field {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	margin-bottom: var(--s-3);
	flex-wrap: wrap;
}
.book-modal-field .setting-label {
	min-width: 6.5rem;
	flex-shrink: 0;
}
/* Modal needs more breathing room than the compact .setting-input default. */
.book-modal-field .setting-input {
	width: auto;
	min-width: 10rem;
}
.book-modal-field input[type='date'] {
	min-width: 12rem;
	padding: var(--s-2) var(--s-3);
}
.book-modal-stars .star {
	font-size: 20px;
}
.book-modal-notes {
	flex: 1;
	min-width: 12rem;
	font: inherit;
	color: var(--c-fg);
	background: var(--c-surface);
	border: 1px solid var(--c-border);
	border-radius: var(--r-2);
	padding: var(--s-2) var(--s-3);
	resize: vertical;
}
.book-modal-notes:focus {
	outline: none;
	border-color: var(--c-accent);
}
.book-modal-actions {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	margin-top: var(--s-4);
	padding-top: var(--s-4);
	border-top: 1px solid var(--c-border);
}
.book-modal-actions .spacer {
	flex: 1;
}
.book-modal-footer {
	margin-top: var(--s-3);
	text-align: right;
}

.book-row-actions {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	flex-shrink: 0;
}
.book-row .book-row-details {
	color: var(--c-fg-dim);
}
.book-row .book-row-details:hover {
	color: var(--c-accent);
}

.library-row,
.release-row {
	cursor: pointer;
}
.library-row:focus-visible,
.release-row:focus-visible {
	outline: 1px solid var(--c-accent);
	outline-offset: 2px;
}
.modal .button.danger {
	border-color: var(--c-danger);
	color: var(--c-danger);
}
.modal .button.danger:hover,
.modal .button.danger:focus-visible {
	border-color: var(--c-danger);
	color: #d68078;
	background: rgba(198, 106, 94, 0.08);
}

@keyframes modal-fade-in {
	from {
		opacity: 0;
	}
	to {
		opacity: 1;
	}
}
@keyframes modal-rise {
	from {
		opacity: 0;
		transform: translate(-50%, calc(-50% + 16px));
	}
	to {
		opacity: 1;
		transform: translate(-50%, -50%);
	}
}

/* ──────────────── Phase 6.8 micro-interactions ──────────────── */

/*
 * Mark-read: when a row gains `is-read`, the gold left-edge bar
 * appears via `border-left-color` transition (the existing border
 * was always 3px transparent — see book-row / library-row).
 * Pair the bar with a subtle title-color settle from the just-read
 * brightness back to the muted variant. Both are CSS-only state
 * transitions; no animation, no keyframes.
 */
.book-row,
.release-row,
.library-row {
	transition:
		border-color var(--motion-slow),
		background var(--motion-fast);
}
.book-row .book-title,
.release-row .release-title,
.library-row .library-title {
	transition: color var(--motion-slow);
}

/*
 * Star ripple on commit. When a star button is clicked, a brief
 * scale pulse acknowledges the rating. Triggered by adding the
 * `.just-clicked` class for one animation cycle (handled in JS).
 */
@keyframes star-ripple {
	0% {
		transform: scale(1);
	}
	50% {
		transform: scale(1.18);
	}
	100% {
		transform: scale(1);
	}
}
.star.just-clicked {
	animation: star-ripple 220ms ease-out;
}

/*
 * Loading dot pulse. Replaces bare "loading…" text on slow fetches.
 * Three muted dots that fade in sequence — calmer than a spinner,
 * carries the "we're working" signal at a glance.
 */
.loading-row {
	padding: var(--s-4) 0;
}
.loading-dots {
	display: inline-flex;
	gap: 4px;
	color: var(--c-fg-muted);
	font-family: var(--f-mono);
	letter-spacing: 0.04em;
}
.loading-dots::before,
.loading-dots > span,
.loading-dots::after {
	font-size: 1.1em;
	line-height: 1;
	animation: loading-dot-pulse 1200ms ease-in-out infinite;
}
.loading-dots::before {
	content: '·';
}
.loading-dots::after {
	content: '·';
	animation-delay: 400ms;
}
.loading-dots > span {
	animation-delay: 200ms;
}
@keyframes loading-dot-pulse {
	0%,
	80%,
	100% {
		opacity: 0.25;
	}
	40% {
		opacity: 1;
	}
}

/*
 * Follow button cross-fade. When `onFollow` succeeds, the imperative
 * text swap from "follow" → "followed" looks jumpy. Adding a brief
 * opacity dip during the swap softens it without complicating the
 * JS. CSS-only — the JS still does `textContent =`, the transition
 * handles the visual settle.
 */
.button.is-syncing {
	opacity: 0.6;
	pointer-events: none;
}
.button {
	transition:
		opacity var(--motion-fast),
		background var(--motion-fast);
}

@media (prefers-reduced-motion: reduce) {
	.modal-backdrop,
	.modal,
	.star.just-clicked,
	.loading-dots::before,
	.loading-dots::after,
	.loading-dots > span {
		animation: none;
	}
	.book-row,
	.release-row,
	.library-row,
	.book-row .book-title,
	.release-row .release-title,
	.library-row .library-title,
	.button {
		transition: none;
	}
}

/* ──────────────── responsive: mobile bottom tabs ──────────────── */

@media (max-width: 720px) {
	.app.signed-in {
		min-height: 100vh;
		min-height: 100dvh;
	}
	.app-content {
		padding: var(--s-5) var(--s-4) calc(var(--s-12) + env(safe-area-inset-bottom));
	}
	.app-nav {
		position: fixed;
		bottom: 0;
		left: 0;
		right: 0;
		z-index: 20;
		padding-bottom: env(safe-area-inset-bottom);
		/* Solid warm-ink behind the nav so the brass tab labels are
		   readable; the scrim ::before above it does the fade. The
		   solid + blur combo lets covers / text glimpse-through but
		   keeps the chrome legible. */
		background: rgba(14, 10, 6, 0.92);
		backdrop-filter: blur(12px);
		-webkit-backdrop-filter: blur(12px);
		/* No top-border — the scrim ::before fades content into the nav
		   instead of cutting it off with a hard edge. */
		border-top: 0;
	}
	/* Gradient scrim above the nav so content fades into the bar rather
	   than slamming into an opaque edge. Pure CSS, sits ABOVE the nav,
	   non-interactive. Phase 6.1 polish-agent finding. */
	.app-nav::before {
		content: '';
		position: absolute;
		left: 0;
		right: 0;
		bottom: 100%;
		height: 28px;
		background: linear-gradient(to bottom, transparent, var(--c-bg));
		pointer-events: none;
	}
	.nav-tab {
		padding: var(--s-3) var(--s-2);
		min-height: 56px; /* mobile touch target */
	}
	.nav-tab.active {
		border-top-color: var(--c-accent);
	}
	.row-photo {
		width: 36px;
		height: 36px;
	}
	.books-pane-content {
		padding-left: var(--s-4);
	}
}

/* ──────────────── responsive: desktop top nav ──────────────── */

@media (min-width: 721px) {
	/* Header + nav stick together at the top of the viewport. Wrapping
	 * them in `.app-top` keeps both anchored without the
	 * sticky-on-sticky positioning math. */
	.app-top {
		position: sticky;
		top: 0;
		z-index: 10;
		background: var(--c-bg);
	}
	/* Desktop chrome layout (Phase 6.5 refinement): the wordmark sits
	 * centered, the nav tabs sit centered, sign-out is tucked top-right.
	 * Header becomes a 3-col grid so brand truly centers regardless of
	 * the right-side user-mini width. */
	.app-header {
		display: grid;
		grid-template-columns: 1fr auto 1fr;
		align-items: center;
	}
	.app-header .brand-mini {
		grid-column: 2;
		justify-self: center;
	}
	.app-header .user-mini {
		grid-column: 3;
		justify-self: end;
	}
	.app-nav {
		max-width: var(--content-max);
		margin: 0 auto;
		width: 100%;
		border-top: 0;
		border-bottom: 1px solid var(--c-border);
		padding: 0 var(--s-5);
		justify-content: center;
	}
	.nav-tab {
		flex: 0 0 auto;
		padding: var(--s-3) var(--s-5);
		border-top: 0;
		border-bottom: 2px solid transparent;
	}
	.nav-tab.active {
		border-bottom-color: var(--c-accent);
	}
}

/* ──────────────── responsive: wide desktop ──────────────── */

/* ──────────────── responsive: wide desktop with sidebar (Phase 6.5) ──────────────── */
/*
 * At ≥ 1024px the app shell becomes a 2-column grid: a 260px left rail
 * + the content column. The rail carries the followed-authors quick-jump
 * list, mounted globally so every view shares it. Mobile + narrow desktop
 * (<1024px) is unchanged — sidebar is `display: none`.
 *
 * Two thresholds:
 *   - 1024px: sidebar appears. Content stays at 38rem so the sidebar +
 *     content + margins all fit on a typical laptop screen without
 *     feeling cramped.
 *   - 1240px: content opens to 56rem for readers on wide monitors. Below
 *     this threshold we'd overflow if the content also widened.
 */
@media (min-width: 1024px) {
	.app-shell {
		display: grid;
		grid-template-columns: 260px 1fr;
		gap: var(--s-6);
		max-width: 1200px;
		margin: 0 auto;
		width: 100%;
		padding: 0 var(--s-5);
		box-sizing: border-box;
	}
	/* Header + nav need to align with the shell on desktop so the
	   sticky chrome doesn't sit off-center from the content + rail. */
	.app-header,
	.app-nav {
		max-width: 1200px;
		margin: 0 auto;
		width: 100%;
		box-sizing: border-box;
	}
	.app-sidebar {
		display: block;
		padding-top: var(--s-6);
		border-right: 1px solid var(--c-border);
		padding-right: var(--s-5);
		position: sticky;
		top: var(--s-12); /* clear the sticky .app-top */
		max-height: calc(100vh - var(--s-12) - var(--s-4));
		overflow-y: auto;
		align-self: start;
	}
	.app-content {
		max-width: none; /* grid column owns the width now */
		margin: 0;
		padding-left: 0;
		padding-right: 0;
	}
	#push-banner {
		max-width: 1200px;
		margin: 0 auto;
	}
}

@media (min-width: 1240px) {
	.app-shell,
	.app-header,
	.app-nav,
	#push-banner {
		max-width: 80rem; /* ~1280px */
	}
	.app-content {
		max-width: 56rem;
	}
}

/* ──────────────── sidebar internals ──────────────── */

.sidebar-block {
	display: flex;
	flex-direction: column;
	gap: var(--s-3);
}
.sidebar-label {
	font-family: var(--f-mono);
	font-size: var(--t-xs);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--c-fg-dim);
}
.sidebar-empty {
	font-size: var(--t-sm);
}
.sidebar-author-list {
	display: flex;
	flex-direction: column;
	gap: 2px;
}
.sidebar-author {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	padding: var(--s-2) var(--s-2);
	margin: 0 calc(-1 * var(--s-2));
	border-radius: var(--r-1);
	color: var(--c-fg);
	font-size: var(--t-sm);
	text-decoration: none;
	border-bottom: 0;
	transition:
		background var(--motion-fast),
		color var(--motion-fast);
}
.sidebar-author:hover {
	background: var(--c-surface);
	color: var(--c-accent-hover);
	border-bottom: 0; /* override base.css <a> hover underline */
}
.sidebar-author-photo {
	width: 28px;
	height: 28px;
	border-radius: var(--r-1);
	object-fit: cover;
	background: var(--c-surface-2);
	border: 1px solid var(--c-border);
	box-shadow: inset 0 0 0 1px rgba(196, 160, 96, 0.18);
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	font-family: var(--f-serif);
	font-variant-caps: all-small-caps;
	font-size: var(--t-sm);
	color: var(--c-fg-muted);
}
.sidebar-author-name {
	font-family: var(--f-display);
	font-weight: 500;
	font-variation-settings:
		'opsz' 14,
		'SOFT' 50,
		'WONK' 0;
	letter-spacing: -0.003em;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	min-width: 0;
}
