/*
 * Broadsheet design system - the canonical token set and shared component classes for every
 * SweetRPG frontend. Ported from Claude Design's "SweetRPG catalog app" project (RPG
 * Catalogue.dc.html), merged here with the app-specific additions (banners, tags, tables, forms,
 * main-web's landing layout) that grew up around the earlier placeholder version of this file.
 * See sweetrpg/platform's openspec/changes/consolidate-shared-design-system for the merge
 * decisions - notably: `.card`/`.card-title`/`.card-body`/`.card-meta`/`.card-grid`/
 * `.card-cover*` and `.nav`/`.nav-brand`/`.nav-logo*` keep their pre-merge definitions rather
 * than Broadsheet's, since catalog-web's card grid (10 templates) and every app's top nav
 * already render against them in production - swapping either would be a visible regression,
 * not a fidelity improvement.
 */

@import url('https://fonts.googleapis.com/css2?family=Source+Serif+4:ital,wght@0,400;0,600;1,400&display=swap');

:root {
	--color-bg: #f3f2f2;
	--color-surface: #eae9e9;
	--color-text: #201e1d;
	--color-accent: #0088b0;
	--color-accent-2: #d6006c;
	--color-divider: color-mix(in srgb, #201e1d 16%, transparent);

	--color-neutral-100: #f8f4f4;
	--color-neutral-200: #eae7e7;
	--color-neutral-300: #d7d3d3;
	--color-neutral-400: #bab6b6;
	--color-neutral-500: #9b9797;
	--color-neutral-600: #7d7979;
	--color-neutral-700: #605d5d;
	--color-neutral-800: #444141;
	--color-neutral-900: #2d2b2b;

	--color-accent-100: #e9f8ff;
	--color-accent-200: #cbeeff;
	--color-accent-300: #99e0ff;
	--color-accent-400: #62c5ee;
	--color-accent-500: #38a6cf;
	--color-accent-600: #1186ac;
	--color-accent-700: #006786;
	--color-accent-800: #004961;
	--color-accent-900: #0a303e;

	--color-accent-2-100: #fff1f4;
	--color-accent-2-200: #ffdee6;
	--color-accent-2-300: #ffc0d0;
	--color-accent-2-400: #ff90b1;
	--color-accent-2-500: #ff458e;
	--color-accent-2-600: #d82071;
	--color-accent-2-700: #aa0b56;
	--color-accent-2-800: #790e3d;
	--color-accent-2-900: #4b1528;

	--color-process-yellow: #edbb00;

	--font-heading: "Source Serif 4", system-ui, sans-serif;
	--font-heading-weight: 600;
	--font-body: "Source Serif 4", system-ui, sans-serif;

	--space-1: 5.0px;
	--space-2: 10.0px;
	--space-3: 15.0px;
	--space-4: 20.0px;
	--space-6: 30.0px;
	--space-8: 40.0px;

	--radius-sm: 1px;
	--radius-md: 2px;
	--radius-lg: 4px;
	--radius-control: 8px;

	--shadow-sm: 0 1px 2px color-mix(in srgb, #2d2b2b 14%, transparent);
	--shadow-md: 0 3px 10px color-mix(in srgb, #2d2b2b 16%, transparent);
	--shadow-lg: 0 12px 32px color-mix(in srgb, #2d2b2b 22%, transparent);
}

/* Dark-theme overrides, applied via `data-theme="dark"` on <html> - set by the anti-flash
 * bootstrap script each consuming frontend's <head> runs before first paint (see main-web's
 * templates/main.html for the reference implementation). Covers every token/component this file
 * defines above; accent colors are left as-is since they already have enough contrast against
 * the dark background.
 */
[data-theme="dark"] {
	--color-bg: #1a1918;
	--color-surface: #26241f;
	--color-text: #f0efee;
	--color-divider: #423f3b;

	--color-neutral-100: #26241f;
	--color-neutral-200: #302d28;
	--color-neutral-300: #3a3836;
	--color-neutral-400: #4c4844;
	--color-neutral-500: #666158;
	--color-neutral-600: #a39d92;
	--color-neutral-700: #bcb6ab;
	--color-neutral-800: #d8d3c9;
	--color-neutral-900: #f0efee;
}

*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font-size: 15px; line-height: 1.55; font-weight: 400; background: var(--color-bg); color: var(--color-text); font-family: var(--font-body); }
h1, h2, h3, h4, h5, h6 {
	font-family: var(--font-heading); font-weight: var(--font-heading-weight);
	line-height: 1.12; letter-spacing: -0.015em; margin: 0 0 var(--space-2);
}
h1 { font-size: 42px; }
h2 { font-size: 32px; }
h3 { font-size: 25px; }
h4 { font-size: 20px; }
h5 { font-size: 16px; }
h6 { font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase; }
p { margin: 0 0 var(--space-3); }
a { color: color-mix(in srgb, var(--color-accent) 80%, black); text-decoration: none; }
a:hover { color: var(--color-accent); text-decoration: underline; }
img { display: block; max-width: 100%; }
figure { margin: 0; }
figcaption {
	font-size: 11px; margin-top: var(--space-1);
	color: color-mix(in srgb, var(--color-text) 55%, transparent);
}
.text-muted { color: color-mix(in srgb, var(--color-text) 55%, transparent); }
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
::selection { background: color-mix(in srgb, var(--color-accent) 30%, transparent); }

.hr {
	height: 1px; border: 0; margin: var(--space-4) 0;
	background: var(--color-divider);
}

/* main-web's landing page layout (hero, card grid, footer) - not part of the shared design
 * system itself, kept here only because it's already served from this file; see design.md's
 * non-goals for why relocating it is out of scope for this change. */
.landing {
	min-height: 100vh;
	background: var(--color-bg);
	color: var(--color-text);
	transition: background 0.2s, color 0.2s;
}
.landing-container {
	max-width: 1160px;
	margin: 0 auto;
	padding: 0 clamp(20px, 5vw, 64px) 100px;
}
.landing-hero {
	padding: min(14vh, 140px) 0 60px;
	text-align: center;
	display: flex;
	flex-direction: column;
	align-items: center;
}
.landing-hero > a {
	display: block;
	text-decoration: none !important;
	margin-bottom: 8px;
}
.landing-logo {
	width: min(220px, 32vw);
	height: auto;
	display: block;
}
[data-theme="light"] .landing-logo-dark { display: none; }
[data-theme="dark"] .landing-logo-light { display: none; }
.landing-title {
	font-family: var(--font-heading);
	font-weight: 700;
	font-size: clamp(56px, 9vw, 110px);
	line-height: 0.95;
	letter-spacing: -0.02em;
}
.landing-title-sweet { color: var(--color-text); }
.landing-title-rpg { color: #0a9845; }
.landing-tagline {
	font-size: 19px;
	font-style: italic;
	max-width: 52ch;
	margin: 20px 0 0;
	opacity: 0.8;
}
.landing-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
	grid-auto-rows: 1fr;
	align-items: stretch;
	column-gap: 28px;
	row-gap: clamp(28px, 6vw, 56px);
	padding-top: 20px;
}
.landing-card-link { text-decoration: none; color: inherit; }
.landing-card-link:hover { text-decoration: none; color: inherit; }
.landing-card {
	position: relative;
	height: calc(100% + 20px);
	display: flex;
	flex-direction: column;
	gap: 14px;
	/* Pre-merge this came from broadsheet.css's coexisting .card padding (main.css's own
	 * .card never set it, so it wasn't overridden); lost when .card was merged without it. */
	padding: var(--space-3);
	cursor: pointer;
	overflow: hidden;
	border-radius: 8px;
}
.landing-card-title { font-size: 30px; color: #0a9845; }
.landing-card-body { margin-top: 8px; }
.landing-card-spacer { flex: 1; }
.landing-card-tag { align-self: flex-start; }
.landing-footer-sentinel {
	height: 1px;
}
.landing-footer {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 14px;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 2px;
	pointer-events: none;
	opacity: 1;
	transition: opacity 0.2s;
}
.landing-footer--faded {
	opacity: 0;
}
.landing-footer-line {
	font-size: 12px;
	opacity: 0.55;
	pointer-events: auto;
}

.nav {
	display: flex;
	align-items: center;
	gap: 20px;
	padding: 16px clamp(20px, 5vw, 64px);
	border-bottom: 1px solid var(--color-neutral-300);
}
.nav-brand {
	display: flex;
	align-items: center;
	gap: 8px;
	font-weight: 700;
	font-size: 18px;
}
.nav-brand a { color: var(--color-text) !important; text-decoration: none !important; }
.nav-logo { width: 28px; height: 28px; display: block; }
[data-theme="light"] .nav-logo-dark { display: none; }
[data-theme="dark"] .nav-logo-light { display: none; }
.nav-spacer { flex: 1; }
.nav-search .input { max-width: 240px; }

/* Browse pages' header toolbar (search + sort on the left, Add/Bulk-add buttons clumped on
 * the far right) - persons/publishers/studios/licenses browse.leaf all use this class but it
 * had no layout rule at all, so the search form, sort select, and action buttons just stacked
 * in document order with no relationship to each other. */
.page-header-toolbar {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 12px;
	margin-bottom: 16px;
}
.page-header-toolbar .nav-search {
	display: flex;
	align-items: center;
	gap: 8px;
	margin-right: auto;
}
.nav-user { font-size: 14px; white-space: nowrap; color: var(--color-neutral-600); }
.nav-logout-form { margin: 0; }
.nav-logout-form .btn { font: inherit; }

.input {
	width: 100%; min-height: 36px; padding: 6px 10px; font: inherit;
	font-size: 14px; color: var(--color-text); caret-color: var(--color-accent);
	background: var(--color-surface);
	border: 1px solid var(--color-divider); border-radius: var(--radius-md);
}
.input::placeholder { color: color-mix(in srgb, var(--color-text) 65%, transparent); opacity: 1; }
.input:hover { border-color: color-mix(in srgb, var(--color-text) 45%, transparent); }
.input:focus-visible { border-color: var(--color-accent); outline-offset: 0; }
textarea.input { min-height: 90px; resize: vertical; }

.field > label {
	display: block; font-size: 12px; margin-bottom: 5px;
	color: color-mix(in srgb, var(--color-text) 70%, transparent);
}
.radio { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-size: 14px; }
.radio input, .seg-opt input {
	position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;
}
.radio .dot {
	width: 16px; height: 16px; flex: none; border-radius: 50%;
	border: 1.5px solid var(--color-divider);
}
.radio:hover .dot { border-color: var(--color-accent); }
.radio input:checked + .dot {
	border-color: var(--color-accent); background: var(--color-accent);
	box-shadow: inset 0 0 0 4px var(--color-bg);
}
.radio input:focus-visible + .dot { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.seg {
	display: inline-flex; overflow: hidden;
	border: 1px solid var(--color-divider); border-radius: var(--radius-md);
}
.seg-opt {
	display: inline-flex; align-items: center; gap: 6px;
	padding: 7px 12px; font-size: 13px; cursor: pointer;
}
.seg-opt + .seg-opt { border-left: 1px solid var(--color-divider); }
.seg-opt:has(input:checked) { background: var(--color-accent); color: var(--color-bg); }
.seg-opt:not(:has(input:checked)):hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.seg-opt:has(input:focus-visible) { outline: 2px solid var(--color-accent); outline-offset: -2px; }

.btn {
	display: inline-flex; align-items: center; justify-content: center; gap: 6px;
	cursor: pointer; text-decoration: none;
	font-family: var(--font-heading); font-weight: var(--font-heading-weight);
	font-size: 14px; line-height: 1.2; color: var(--color-text);
	background: transparent; border: 1px solid transparent;
	padding: var(--space-2) calc(var(--space-3) * 1.2);
	border-radius: var(--radius-control);
}
.btn svg { display: block; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn-primary { background: var(--color-accent); color: var(--color-bg); }
/* Derived from --color-accent via color-mix, not the fixed --color-accent-600/700 ramp steps -
 * those don't track a per-app --color-accent override (see requirements/../catalog/theme.css),
 * so a hover/active state on an overridden accent (e.g. catalog-web's green) rendered in the
 * shared ramp's unrelated teal instead. */
.btn-primary:hover { background: color-mix(in srgb, var(--color-accent) 85%, black); }
.btn-primary:active { background: color-mix(in srgb, var(--color-accent) 70%, black); }
.btn-secondary { border-color: var(--color-divider); }
.btn-secondary:hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.btn-secondary:active { background: color-mix(in srgb, var(--color-text) 14%, transparent); }
.btn-ghost { color: var(--color-accent); padding-inline: var(--space-1); background: transparent; border-color: var(--color-neutral-300); }
.btn-ghost:hover { background: color-mix(in srgb, var(--color-accent) 10%, transparent); }
.btn-ghost:active { background: color-mix(in srgb, var(--color-accent) 18%, transparent); }
.btn-icon { width: 36px; height: 36px; padding: 0; }
.btn-block { width: 100%; margin-top: var(--space-2); }

.page { max-width: 1160px; margin: 0 auto; padding: 0 clamp(20px, 5vw, 64px) 80px; }

.hero { padding: 56px 0 40px; }
.hero h1 { font-size: clamp(40px, 6vw, 72px); line-height: 1.05; margin: 0 0 18px; }
.hero-tagline { font-size: 18px; line-height: 1.6; max-width: 56ch; font-style: italic; }
.hero-actions { display: flex; gap: 10px; flex-wrap: wrap; }

.stats-bar {
	display: flex;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: 10px 28px;
	padding: 10px 0;
	margin: 24px 0 40px;
	border-top: 2px solid var(--color-text);
	border-bottom: 1px solid var(--color-text);
	font-size: 13px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

.section-label {
	display: block;
	font-size: 13px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	opacity: 0.55;
	margin-bottom: 14px;
}

.page-header { padding: 48px 0 24px; }
.page-header h2 { font-size: 34px; margin: 0 0 20px; }

/* Entity browse pages' search/sort form plus an "Add X" action - keeps both on one row, action
 * pushed to the far right. Shared across licenses/studios/persons/publishers browse.leaf. */
.page-header-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }

/* catalog-web's cover-image card grid - kept as its own definition rather than Broadsheet's
 * (unrelated, padding-based) .card; see this file's header comment. */
.card-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
	gap: 24px;
	padding-bottom: 40px;
}
.card {
	display: block;
	background: var(--color-surface);
	border: 1px solid var(--color-neutral-300);
	border-radius: 8px;
	overflow: hidden;
	color: var(--color-text) !important;
	text-decoration: none !important;
}
.card-cover {
	position: relative;
	aspect-ratio: 3 / 4;
	background: var(--color-neutral-300);
	display: flex;
	align-items: center;
	justify-content: center;
	color: var(--color-neutral-600);
	font-size: 12px;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	text-align: center;
	padding: 10px;
	overflow: hidden;
}
/*
 * A cover image - live if the volume has one stored (assets-web's /asset/<kind>/<id> route),
 * otherwise a placeholder (img/catalog/cover-placeholder-*.png) swapped in by consuming
 * templates' inline `onerror` handler on a 404 (no cover on file).
 */
.card-cover-img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}
.card-body { padding: 14px 16px 18px; }
.card-title { font-size: 17px; font-weight: 600; }
.card-meta { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
.card-kicker { font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-accent); }
.elev-sm { box-shadow: var(--shadow-sm); }
.elev-md { box-shadow: var(--shadow-md); }
.elev-lg { box-shadow: var(--shadow-lg); }

.tag {
	display: inline-flex; align-items: center; font-size: 11px;
	letter-spacing: 0.02em; padding: 3px 10px;
	/* Capsule, not broadsheet.css's near-square radius-md - this was the shape actually
	 * rendering everywhere pre-merge (main.css's own value always won this property). */
	border-radius: 999px;
}
.tag-accent { background: color-mix(in srgb, var(--color-accent) 12%, var(--color-bg)); color: color-mix(in srgb, var(--color-accent) 70%, black); }
.tag-accent-2 { background: var(--color-accent-2-100); color: var(--color-accent-2-800); }
.tag-neutral { background: var(--color-neutral-100); color: var(--color-neutral-800); }
.tag-outline { border: 1px solid var(--color-accent); color: var(--color-accent); }
.tag-cloud { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
/* Distinct from .tag-outline: red border reads as "currently down", not "forthcoming". */
.tag-danger { border: 1px solid #f0a3a3; color: #7a1212; cursor: help; }
[data-theme="dark"] .tag-danger { border-color: #7a2e2e; color: #f5a3a3; }
/* Severity-style tag variants, matching .banner-critical/.banner-warning's palette below via the
 * same color-mix approach as .tag-danger, so a status chip and a banner agree on what "critical"
 * or "warning" looks like. */
.tag-critical { background: color-mix(in srgb, #f0a3a3 25%, transparent); border: 1px solid #f0a3a3; color: #7a1212; }
[data-theme="dark"] .tag-critical { background: color-mix(in srgb, #7a2e2e 45%, transparent); border-color: #7a2e2e; color: #f5a3a3; }
.tag-status-expired { background: color-mix(in srgb, #f0c669 25%, transparent); border: 1px solid #f0c669; color: #7a5a00; }
[data-theme="dark"] .tag-status-expired { background: color-mix(in srgb, #7a5a00 45%, transparent); border-color: #7a5a00; color: #f0c669; }
.tag-success { background: color-mix(in srgb, #a3d9a3 25%, transparent); border: 1px solid #a3d9a3; color: #1b5e1b; }
[data-theme="dark"] .tag-success { background: color-mix(in srgb, #2e7a3a 45%, transparent); border-color: #2e7a3a; color: #a3f0b8; }

/* admin-api banner messages (severity info/warning/critical), rendered by every frontend that
 * shows them - centralized so a severity's color only has one definition to keep in sync with
 * the .tag-critical/.tag-status-expired variants above. */
.banner-region { display: flex; flex-direction: column; gap: 4px; padding: 8px 16px 0; max-width: 80%; margin-inline: auto; }
.banner { border-radius: var(--radius-md); padding: 10px 16px; font-size: 0.9rem; border: 1px solid transparent; }
.banner-info { background: #e6f2ff; border-color: #99c9ff; color: #0a3d7a; }
.banner-warning { background: #fff6e0; border-color: #f0c669; color: #7a5a00; }
.banner-critical { background: #fde8e8; border-color: #f0a3a3; color: #7a1212; font-weight: 600; }
[data-theme="dark"] .banner-info { background: #16273d; border-color: #2c4d7a; color: #a9cdfa; }
[data-theme="dark"] .banner-warning { background: #3d3312; border-color: #7a6425; color: #f5d78a; }
[data-theme="dark"] .banner-critical { background: #3d1414; border-color: #7a2e2e; color: #f5a3a3; }

.empty-state { opacity: 0.6; padding: 20px 0; }
.empty-state-inline { opacity: 0.55; font-style: italic; font-size: 14px; }
.table-empty { text-align: center; border-bottom: none; }

/* Data tables (admin-web's banners/users/maintenance-modes lists). */
.table { width: 100%; border-collapse: collapse; font-size: 14px; }
.table th {
	text-align: left; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
	color: color-mix(in srgb, var(--color-text) 60%, transparent);
	padding: var(--space-2); border-bottom: 1px solid var(--color-divider);
}
.table td {
	padding: var(--space-2);
	border-bottom: 1px solid color-mix(in srgb, var(--color-text) 8%, transparent);
}
.table tbody tr:hover { background: color-mix(in srgb, var(--color-text) 4%, transparent); }
.table-actions { display: flex; gap: 8px; align-items: center; }

/* Admin forms (admin-web's banner/maintenance-mode create-edit forms). */
.form {
	display: grid;
	grid-template-columns: 160px minmax(0, 1fr);
	gap: 14px 16px;
	align-items: start;
	max-width: 640px;
	margin-top: 20px;
}
.form-label { text-align: right; font-size: 14px; font-weight: 600; padding-top: 9px; }
.form .input, .form select, .form textarea { width: 100%; }
.form-error { grid-column: 2; color: #7a1212; font-size: 13px; margin: -6px 0 0; }
.form-hint { grid-column: 2; color: color-mix(in srgb, var(--color-text) 55%, transparent); font-size: 13px; margin: -6px 0 0; }
.form-actions { grid-column: 2; display: flex; gap: 10px; margin-top: 8px; }
.form > button[type="submit"] { grid-column: 2; justify-self: start; margin-top: 8px; }

.back-link { display: inline-block; font-size: 13px; letter-spacing: 0.04em; text-transform: uppercase; margin-top: 40px; }

.detail-layout {
	display: grid;
	grid-template-columns: minmax(0, 280px) minmax(0, 1fr);
	gap: 40px;
	margin-top: 24px;
	align-items: start;
	padding-bottom: 60px;
}
.detail-cover { aspect-ratio: 3 / 4; font-size: 13px; }
.detail-description { font-size: 16px; line-height: 1.65; max-width: 60ch; opacity: 0.75; }
.detail-description-empty { opacity: 0.55; font-style: italic; }
.detail-notes { font-size: 14px; line-height: 1.6; max-width: 60ch; opacity: 0.75; }
.detail-block { margin: 24px 0; }

/* licenses/detail.leaf's two-column layout: metadata list on the left, volumes on the right,
 * both clipped to the same height so neither column's length pushes the license text down. */
.license-columns {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 320px);
	gap: 40px;
	margin: 24px 0;
	align-items: start;
}
.license-meta {
	display: grid;
	grid-template-columns: max-content minmax(0, 1fr);
	gap: 10px 16px;
	max-height: 360px;
	overflow-y: auto;
	padding-right: 8px;
}
.license-meta-label { text-align: right; font-size: 13px; font-weight: 600; opacity: 0.7; white-space: nowrap; }
.license-meta-value { font-size: 14px; }
.license-notes {
	max-height: 120px;
	overflow-y: auto;
	white-space: pre-wrap;
	line-height: 1.5;
	padding: 8px 10px;
	border: 1px solid var(--color-neutral-300);
	border-radius: 6px;
}
.license-volumes-panel { max-height: 360px; overflow-y: auto; padding-right: 8px; }
.license-volumes-panel .card-grid { grid-template-columns: 1fr; padding-bottom: 0; gap: 12px; }
.license-text-panel {
	max-height: 50vh;
	overflow-y: auto;
	white-space: pre-wrap;
	font-size: 14px;
	line-height: 1.6;
	padding: 16px;
	border: 1px solid var(--color-neutral-300);
	border-radius: 8px;
}
.license-text-panel details { margin-bottom: 12px; }
.license-text-panel details:last-child { margin-bottom: 0; }
.license-text-panel summary {
	font-size: 13px;
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	opacity: 0.7;
	cursor: pointer;
}
.review { margin-bottom: 16px; }
.review-stars { color: var(--color-accent); letter-spacing: 1px; }

/* catalog-web's fluid volume-edit page: inline per-field edit affordances laid out to match
 * the detail page's own typography, not a traditional labeled-grid form. */
.icon-btn {
	background: none;
	border: none;
	padding: 4px;
	margin: 0;
	cursor: pointer;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	vertical-align: middle;
	border-radius: 50%;
}
/* Every rule above sets `display` unconditionally, which beats the UA `[hidden]{display:none}`
 * default on specificity ties (author stylesheets always win ties over UA ones) - without this,
 * a hidden .icon-btn/.field-input still renders. Same fix needed wherever a page's own CSS also
 * forces `display` on one of these classes (see catalog-web's edit.leaf `.cover-overlay-controls
 * .icon-btn`, `.tag-picker`, `.contributor-dialog`) - each needs its own `[hidden]` override too. */
.icon-btn[hidden] { display: none; }
.icon-btn:hover { background: var(--color-neutral-300); }
.icon-btn img { width: 16px; height: 16px; display: block; }
/* Destructive action variant (delete/restore) - the same danger red used for
 * .form-error/.tag-danger elsewhere, not the accent-tinted background every other icon button
 * gets. Hand-drawn icons with their own explicit stroke color (edit.svg, history.svg) don't
 * need the .icon-mask treatment below and shouldn't get .icon-btn-danger. */
.icon-btn-danger { background: color-mix(in srgb, #7a1212 20%, transparent); }
.icon-btn-danger:hover { background: color-mix(in srgb, #7a1212 32%, transparent); }
/* `filter: invert(1)` (turning a black-fill FontAwesome icon white) read as flat/washed-out
 * against the tinted red background - a CSS mask paints the icon in the same danger red used
 * for .tag-danger/.tag-critical elsewhere (#f0a3a3, the lighter member of that pair) instead,
 * so foreground and background read as the same color family. Needs an empty, content-less
 * element (an <img>'s own painted pixels can't be recolored by mask-image the way a masked
 * background-color can) - see catalog-web's icon-mask markup at each .icon-btn-danger call site.
 */
.icon-btn-danger img { display: none; }
.icon-mask {
  display: block;
  width: 16px;
  height: 16px;
  background-color: #f0a3a3;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}
/* Generic fix for the same default-black-fill problem on a non-destructive FontAwesome icon
 * (e.g. plus.svg) - use directly on the <img> where .icon-btn-danger's semantics don't apply. */
.icon-invert { filter: invert(1); }
.field-row { display: flex; align-items: baseline; gap: 6px; }
.field-row-block { align-items: flex-start; }
.field-actions { display: inline-flex; gap: 2px; }
.field-input { display: block; width: 100%; max-width: 60ch; margin: 4px 0; }
.field-input[hidden] { display: none; }

.auth-page { padding: 48px 0; max-width: 480px; }

.footer {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 8px;
	padding: 24px;
	border-top: 1px solid var(--color-neutral-300);
	font-size: 13px;
	opacity: 0.7;
}
.footer-links { display: flex; gap: 16px; }
.footer-build { margin: 0; font-size: 12px; font-weight: 400; color: var(--color-neutral-600); }

.dialog-backdrop {
	position: fixed; inset: 0; display: grid; place-items: center;
	padding: var(--space-4);
	background: color-mix(in srgb, var(--color-neutral-900) 50%, transparent);
}
.dialog-backdrop[hidden] { display: none; }
.dialog {
	width: min(440px, 100%); display: flex; flex-direction: column; gap: var(--space-3);
	padding: var(--space-4); border-radius: var(--radius-lg);
	background: var(--color-surface); box-shadow: var(--shadow-lg);
}
.dialog-title {
	font-family: var(--font-heading); font-weight: var(--font-heading-weight);
	font-size: 20px;
}
.dialog-body { font-size: 14px; opacity: 0.85; }
.dialog-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-2); }

/* Browse-page pagination footer (persons/publishers/studios/licenses/volumes) - Prev/Next plus a
 * small window of page-number links, all pre-built server-side (see catalog-web's
 * EntityBrowsePagination.swift) so this is pure rendering, no client-side page math. */
.pagination {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	margin-top: 32px;
	flex-wrap: wrap;
}
.pagination-link, .pagination-page {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 2rem;
	height: 2rem;
	padding: 0 10px;
	border-radius: 10px;
	font-size: 14px;
	text-decoration: none;
	color: var(--color-text);
}
.pagination-page:hover, .pagination-link:hover {
	background: var(--color-neutral-300);
}
.pagination-link-disabled {
	opacity: 0.35;
	pointer-events: none;
}
.pagination-page-current {
	background: var(--color-accent);
	color: var(--color-on-accent, #fff);
	font-weight: var(--font-heading-weight);
}

/* Rich hover/focus tooltip - pure CSS, no JS. Put `.tooltip-host` on the trigger itself (make
 * it focusable: add tabindex="0" if it isn't already a button/link) and nest a `.tooltip`
 * inside it holding a `.tooltip-title` and an optional `.tooltip-body` (omit the body when a
 * one-word title is the whole story), or any block content. It shows above the trigger on
 * hover or keyboard focus, including focus landing on a nested control (e.g. a radio inside a
 * label). Adding a tooltip never removes the trigger's `aria-label`.
 *
 * Copy: name the thing in `.tooltip-title`, one plain sentence in `.tooltip-body`.
 *
 * Limitation: the panel is positioned relative to `.tooltip-host`, so a host inside an
 * `overflow: hidden`/`auto` ancestor is clipped. Those call sites need a JS-positioned
 * tooltip instead - out of scope for this CSS-only component.
 *
 * When to use this vs a bare `title` attribute: see `docs/visual-design-conventions.md` in
 * sweetrpg/platform ("Rich tooltips over bare title attributes").
 */
.tooltip-host { position: relative; }
.tooltip {
	position: absolute;
	left: 50%;
	bottom: calc(100% + 8px);
	transform: translateX(-50%) translateY(4px);
	z-index: 200;
	width: max-content;
	max-width: 260px;
	padding: var(--space-2) var(--space-3);
	border-radius: var(--radius-lg);
	background: var(--color-surface);
	color: var(--color-text);
	border: 1px solid var(--color-neutral-300);
	box-shadow: var(--shadow-lg);
	text-align: left;
	font-size: 13px;
	line-height: 1.4;
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
	transition: opacity 120ms ease, transform 120ms ease, visibility 0s linear 120ms;
}
.tooltip::after {
	content: "";
	position: absolute;
	top: 100%;
	left: 50%;
	transform: translateX(-50%);
	border: 6px solid transparent;
	border-top-color: var(--color-surface);
}
.tooltip-host:hover > .tooltip,
.tooltip-host:focus-visible > .tooltip,
.tooltip-host:focus-within > .tooltip {
	opacity: 1;
	visibility: visible;
	transform: translateX(-50%) translateY(0);
	transition: opacity 120ms ease, transform 120ms ease;
}
.tooltip-title {
	display: block;
	font-family: var(--font-heading);
	font-weight: var(--font-heading-weight);
	font-size: 13px;
}
.tooltip-body {
	display: block;
	margin-top: 3px;
	color: var(--color-neutral-600);
}
@media (prefers-reduced-motion: reduce) {
	.tooltip { transition: opacity 120ms ease, visibility 0s linear 120ms; transform: translateX(-50%); }
	.tooltip-host:hover > .tooltip,
	.tooltip-host:focus-visible > .tooltip,
	.tooltip-host:focus-within > .tooltip { transform: translateX(-50%); }
}
