/* Potum — components & layout. Mobile-first; single breakpoint at 820px. */

* {
	box-sizing: border-box;
	margin: 0;
	padding: 0;
}
html,
body {
	background: var(--canvas);
}
body {
	font-family: var(--font-body);
	color: var(--ink);
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
	min-height: 100vh;
	display: flex;
	flex-direction: column;
}
a {
	color: var(--accent-deep);
	text-decoration: none;
}
a:hover {
	color: var(--accent-hover);
}
input,
button,
select,
textarea {
	font-family: inherit;
}
::selection {
	background: #cde6f8;
}
::-webkit-scrollbar {
	width: 10px;
	height: 10px;
}
::-webkit-scrollbar-thumb {
	background: #dedcd3;
	border-radius: 20px;
	border: 3px solid var(--canvas);
}
img {
	max-width: 100%;
}
@keyframes potFade {
	from {
		opacity: 0;
		transform: translateY(6px);
	}
	to {
		opacity: 1;
		transform: none;
	}
}
@keyframes riseIn {
	from {
		opacity: 0;
		transform: translateY(10px);
	}
	to {
		opacity: 1;
		transform: none;
	}
}
@keyframes blobFloatA {
	0%,
	100% {
		transform: translate(0, 0) scale(1);
	}
	50% {
		transform: translate(16px, -12px) scale(1.06);
	}
}
@keyframes blobFloatB {
	0%,
	100% {
		transform: translate(0, 0) scale(1.06);
	}
	50% {
		transform: translate(-16px, 12px) scale(1);
	}
}
@keyframes dotPulse {
	0%,
	100% {
		opacity: 1;
	}
	50% {
		opacity: 0.35;
	}
}
.rise-in {
	animation: riseIn 0.48s ease both;
}
@media (prefers-reduced-motion: reduce) {
	.hero__blob,
	.dot--live {
		animation: none;
	}
}

.mono {
	font-family: var(--font-mono);
}
.wrap {
	max-width: var(--maxw-wide);
	margin: 0 auto;
}

/* ---------------- Top bar ---------------- */
.topbar {
	position: sticky;
	top: 0;
	z-index: 40;
	background: rgba(245, 244, 239, 0.86);
	backdrop-filter: blur(12px);
	border-bottom: 1px solid var(--hairline);
}
.topbar__in {
	max-width: var(--maxw-wide);
	margin: 0 auto;
	padding: 10px 14px;
	display: flex;
	align-items: center;
	gap: 12px;
}
.brand {
	display: flex;
	align-items: center;
	gap: 9px;
	background: none;
	border: none;
	cursor: pointer;
	padding: 0;
	flex-shrink: 0;
}
.brand__logo {
	width: 30px;
	height: 30px;
	border-radius: 9px;
	overflow: hidden;
	display: block;
	flex-shrink: 0;
}
.brand__logo img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}
.brand__name {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 19px;
	letter-spacing: -0.02em;
	color: var(--ink);
}
.pill-beta {
	font-size: 9px;
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--accent);
	background: var(--accent-tint);
	padding: 2px 5px;
	border-radius: 5px;
}

.search {
	flex: 1;
	position: relative;
	max-width: 520px;
}
.search svg {
	position: absolute;
	left: 11px;
	top: 50%;
	transform: translateY(-50%);
}
.search input {
	width: 100%;
	height: 38px;
	border: 1px solid var(--hairline);
	background: var(--surface);
	border-radius: 10px;
	padding: 0 12px 0 34px;
	font-size: 14px;
	color: var(--ink);
	outline: none;
}
.search input:focus {
	border-color: var(--accent-tint-border);
	box-shadow: 0 0 0 3px rgba(79, 163, 220, 0.1);
}

.nav {
	display: none;
	align-items: center;
	gap: 2px;
}
.nav a {
	font-size: 14px;
	font-weight: 600;
	padding: 7px 11px;
	border-radius: 8px;
}
.nav a:hover {
	color: var(--accent-deep);
}
.nav a.is-active {
	color: var(--accent-deep);
}
.btn-contribute {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	margin-left: 6px;
	background: var(--accent);
	color: #fff;
	border: none;
	cursor: pointer;
	font-size: 14px;
	font-weight: 600;
	padding: 8px 13px;
	border-radius: 9px;
}
.btn-contribute:hover {
	color: #fff;
}
.avatar {
	width: 32px;
	height: 32px;
	border-radius: 50%;
	background: var(--accent-tint);
	border: 1px solid var(--accent-tint-border);
	margin-left: 6px;
	display: grid;
	place-items: center;
	font-weight: 700;
	font-size: 13px;
	color: var(--accent-deep);
	font-family: inherit;
	cursor: pointer;
	padding: 0;
}

/* ---- Avatar account menu ---- */
.usermenu {
	position: relative;
	margin-left: 6px;
}
.usermenu .avatar {
	margin-left: 0;
}
.usermenu__pop {
	position: absolute;
	top: calc(100% + 8px);
	/* Anchor to the avatar's right edge: the button sits flush against the
	   viewport's right gutter, so left-anchoring overflows off-screen. */
	right: 0;
	left: auto;
	min-width: 172px;
	max-width: calc(100vw - 2 * var(--gutter));
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: var(--r-btn);
	box-shadow: var(--shadow-lift);
	padding: 6px;
	z-index: 60;
	display: flex;
	flex-direction: column;
	gap: 2px;

	opacity: 0;
	visibility: hidden;
	pointer-events: none;
	transform-origin: top right;
	overflow: hidden;
}
.usermenu__pop[hidden] {
	display: none;
}
.usermenu__form {
	margin: 0;
}
.usermenu__item {
	display: block;
	width: 100%;
	text-align: left;
	padding: 8px 10px;
	border-radius: var(--r-bar);
	font-size: 13px;
	font-weight: 600;
	color: var(--ink-2);
	background: none;
	border: 0;
	font-family: inherit;
	cursor: pointer;
	text-decoration: none;
}
.usermenu__item:hover {
	background: var(--row-hover);
	color: var(--accent-deep);
}
.usermenu__item--danger {
	color: var(--danger-ink);
}
.usermenu__item--danger:hover {
	background: var(--danger-bg);
	color: var(--danger-ink);
}

/* ---------------- Main ---------------- */
main {
	flex: 1;
	width: 100%;
	max-width: var(--maxw-wide);
	margin: 0 auto;
	padding: 16px 14px 96px;
	animation: potFade 0.28s ease both;
}
.page-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 12px;
	margin-bottom: 12px;
}
.page-title {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 22px;
	letter-spacing: -0.02em;
}
.count {
	font-size: 13px;
	color: var(--ink-faint);
	font-weight: 600;
}

/* ---------------- Hero (home) ---------------- */
.hero {
	background: linear-gradient(
		160deg,
		var(--accent-tint-3) 0%,
		var(--canvas) 78%
	);
	border: 1px solid var(--accent-tint-border-2);
	border-radius: var(--r-hero);
	padding: 24px 20px;
	position: relative;
	overflow: hidden;
}
.hero__row {
	position: relative;
	z-index: 2;
}
.hero__in {
	max-width: 560px;
}
.hero__trending {
	display: none;
}
.hero__trending .list-row {
	padding: 8px 12px;
}
.hero__blob {
	position: absolute;
	z-index: 1;
	border-radius: 50%;
	pointer-events: none;
	width: 220px;
	height: 220px;
	filter: blur(34px);
}
.hero__blob--a {
	top: -60px;
	right: -40px;
	background: #dceefb;
	animation: blobFloatA 10s ease-in-out infinite;
}
.hero__blob--b {
	bottom: -70px;
	right: 90px;
	background: #d3e7f6;
	animation: blobFloatB 9.5s ease-in-out infinite reverse;
}
.status-pill {
	display: inline-flex;
	align-items: center;
	gap: 7px;
	background: var(--surface);
	border: 1px solid var(--accent-tint-border-2);
	padding: 4px 10px;
	border-radius: var(--r-pill);
	font-size: 12px;
	font-weight: 600;
	color: var(--accent-deep);
	margin-bottom: 14px;
}
.dot {
	width: 6px;
	height: 6px;
	border-radius: 50%;
}
.dot--live {
	background: var(--success);
	animation: dotPulse 2.2s ease-in-out infinite;
}
.hero h1 {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 30px;
	line-height: 1.02;
	letter-spacing: -0.03em;
	color: var(--ink);
}
.hero p {
	font-size: 15px;
	line-height: 1.5;
	color: var(--ink-muted);
	margin-top: 12px;
}
.hero__search {
	position: relative;
	margin-top: 20px;
	max-width: 460px;
}
.hero__search svg {
	position: absolute;
	left: 14px;
	top: 50%;
	transform: translateY(-50%);
}
.hero__search input {
	width: 100%;
	height: 48px;
	border: 1px solid var(--accent-tint-border);
	background: var(--surface);
	border-radius: 12px;
	padding: 0 14px 0 42px;
	font-size: 15px;
	outline: none;
	box-shadow: 0 4px 14px rgba(79, 163, 220, 0.1);
}

/* ---------------- Banner carousel ---------------- */
.banner-carousel {
	position: relative;
	margin-top: 14px;
	height: 128px;
}
.banner-track {
	position: relative;
	width: 100%;
	height: 100%;
}
.banner-slide {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 14px 16px;
	border-radius: var(--r-card);
	background: var(--tile-bg);
	opacity: 0;
	transform: translateX(10px);
	transition:
		opacity 0.6s ease,
		transform 0.6s ease;
	pointer-events: none;
}
.banner-slide.is-active {
	opacity: 1;
	transform: none;
	pointer-events: auto;
	z-index: 1;
}
.banner-slide__tile {
	flex-shrink: 0;
	width: 34px;
	height: 34px;
	border-radius: 9px;
	background: var(--surface);
	display: grid;
	place-items: center;
	font-family: var(--font-mono);
	font-weight: 700;
	font-size: 11px;
	color: var(--tile-ink);
}
.banner-slide__body {
	min-width: 0;
	flex: 1;
	display: flex;
	flex-direction: column;
	gap: 2px;
}
.banner-slide__eyebrow {
	font-size: 10.5px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--tile-ink);
}
.banner-slide__title {
	font-family: var(--font-display);
	font-weight: 600;
	font-size: 15.5px;
	color: var(--ink);
}
.banner-slide__subtitle {
	font-size: 12.5px;
	color: var(--ink-muted-2);
}
.banner-slide__cta {
	flex-shrink: 0;
	font-size: 12.5px;
	font-weight: 600;
	color: var(--tile-ink);
}
.banner-dots {
	position: absolute;
	left: 0;
	right: 0;
	bottom: -9px;
	display: flex;
	justify-content: center;
	gap: 5px;
	z-index: 2;
}
.banner-dot {
	width: 6px;
	height: 6px;
	border-radius: 20px;
	background: #dedcd3;
	border: none;
	cursor: pointer;
	padding: 0;
	transition: width 0.3s ease;
	box-shadow: none;
}
.banner-dot.is-active {
	width: 22px;
	background: var(--accent);
}

/* ---------------- Recent interactions ---------------- */
.activity-row {
	display: flex;
	gap: 10px;
	padding: 10px 15px;
	border-top: 1px solid var(--hairline-soft);
}
.activity-row__avatar {
	flex-shrink: 0;
	width: 26px;
	height: 26px;
	border-radius: 50%;
	background: var(--accent-tint);
	color: var(--accent-deep);
	display: grid;
	place-items: center;
	font-weight: 700;
	font-size: 11.5px;
}
.activity-row__line {
	font-size: 13px;
	line-height: 1.4;
	color: var(--ink-2);
}
.activity-row__line strong {
	color: var(--ink);
}
.activity-row__snippet {
	font-size: 12px;
	color: var(--ink-muted);
	font-style: italic;
	margin-top: 3px;
}
.activity-row__thumb {
	width: 100px;
	height: 64px;
	margin-top: 5px;
	border-radius: 7px;
	display: grid;
	place-items: center;
	font-family: var(--font-mono);
	font-size: 9px;
	color: var(--ink-faint);
	background-image: linear-gradient(
		135deg,
		rgba(255, 255, 255, 0.55) 25%,
		transparent 25%,
		transparent 50%,
		rgba(255, 255, 255, 0.55) 50%,
		rgba(255, 255, 255, 0.55) 75%,
		transparent 75%
	);
	background-size: 12px 12px;
	background-color: var(--hairline-soft);
}
.activity-row__time {
	font-size: 11px;
	color: var(--ink-faint);
	margin-top: 3px;
}

/* ---------------- Community highlight ---------------- */
.highlight {
	margin-top: 14px;
	background: linear-gradient(135deg, #fbf8ef, #f5f4ef);
	border: 1px solid #ede6d2;
	border-radius: 15px;
	padding: 15px 16px;
}
.highlight__head {
	display: flex;
	align-items: center;
	gap: 7px;
	margin-bottom: 11px;
}
.highlight__title {
	font-family: var(--font-display);
	font-weight: 600;
	font-size: 14px;
	color: var(--ink);
}
.highlight__sub {
	font-size: 12px;
	color: var(--ink-faint);
}
.highlight__body {
	display: flex;
	gap: 11px;
}
.highlight__byline {
	font-size: 13px;
	display: flex;
	align-items: center;
	gap: 8px;
	flex-wrap: wrap;
}
.highlight__byline strong {
	color: var(--ink);
}
.highlight__quote {
	font-size: 13.5px;
	color: var(--ink-2);
	line-height: 1.5;
	margin-top: 5px;
}

/* ---------------- Home two-column ---------------- */
.home-cols {
	display: grid;
	grid-template-columns: 1fr;
	gap: 14px;
	margin-top: 14px;
}
.panel {
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: 15px;
	overflow: hidden;
}
/* Caps the panel at its rows5 height instead of letting it grow with
   however many rows the query returns -- .panel__body scrolls the overflow
   internally so extra rows (e.g. top-10 contributors) don't lengthen the
   page. */
.panel--scroll {
	display: flex;
	flex-direction: column;
	height: 410px;
}
.panel--scroll .panel__body {
	flex: 1;
	min-height: 0;
	overflow-y: auto;
}

#mod-section-held-appearance {
	width: 100%;
	display: grid;
	grid-template-columns: repeat(3, auto);
	grid-template-rows: auto;
	gap: 0.5rem;
}

.panel--pad {
	padding: 0.8rem 0.8rem;
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	justify-content: flex-start;
	align-items: flex-start;
	transition: transform 0.2s ease;
}

.panel--pad:hover {
	transform: translateY(-5px);
	box-shadow: 5px 5px 30px #f4f4f4;
}

.panel__head {
	width: 100%;
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 0.4rem;
}

.panel__title {
	font-family: var(--font-display);
	font-weight: 600;
	font-size: 15px;
	color: var(--ink);
}

.held-img-container {
	aspect-ratio: 3/4;
	overflow: hidden;
}

.held-img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
}

.showcase-col {
	display: flex;
	flex-direction: column;
}
.showcase-col__grid {
	flex: 1;
	min-height: 0;
	overflow: hidden;
	display: flex;
	flex-wrap: wrap;
	align-content: flex-start;
	gap: 10px;
	padding: 0 15px 14px;
}
.showcase-col__item {
	flex: 0 0 calc(50% - 5px);
	min-width: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
	text-decoration: none;
	text-align: center;
}
.showcase-col__img {
	display: block;
	width: 100%;
	aspect-ratio: 1;
	border-radius: 12px;
	overflow: hidden;
	background: var(--surface-sunken);
}
.showcase-col__img img {
	width: 100%;
	height: 100%;
	object-fit: contain;
}
.showcase-col__name {
	font-size: 10.5px;
	font-weight: 600;
	color: var(--ink-2);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	max-width: 100%;
}

.update {
	display: flex;
	gap: 11px;
	padding: 10px 15px;
	border-top: 1px solid var(--hairline-soft);
}
.tag {
	flex-shrink: 0;
	margin-top: 2px;
	font-size: 10px;
	font-weight: 700;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	padding: 3px 6px;
	border-radius: 6px;
	height: fit-content;
}
.tag--new {
	color: var(--success-2);
	background: #ecf7f0;
}
.tag--edit {
	color: var(--accent-deep);
	background: var(--accent-tint);
}
.tag--map {
	color: var(--material-ink);
	background: var(--material-bg);
}
.update__text {
	font-size: 13.5px;
	font-weight: 600;
	color: var(--ink);
	line-height: 1.35;
}
.update__meta {
	font-size: 11.5px;
	color: var(--ink-faint);
	margin-top: 2px;
}

/* space-between: when the merged column (>=820, see home-cols--farm) stretches
   taller than the two cards' own content, the slack splits between them
   instead of piling up under the second card. */
.tools {
	display: flex;
	flex-direction: column;
	gap: 14px;
	justify-content: space-between;
}
.tool {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 9px 10px;
	border: 1px solid #efede6;
	border-radius: 10px;
	background: var(--surface-sunken);
}
.tool:hover {
	border-color: var(--accent-tint-border);
	background: #f4f9fd;
}
.tool__tile {
	width: 28px;
	height: 28px;
	border-radius: 8px;
	background: var(--accent-tint);
	color: var(--accent-deep);
	display: grid;
	place-items: center;
	font-family: var(--font-mono);
	font-weight: 700;
	font-size: 13px;
	flex-shrink: 0;
}
.tool__name {
	display: block;
	font-size: 13.5px;
	font-weight: 600;
	color: var(--ink);
}
.tool__note {
	display: block;
	font-size: 11.5px;
	color: var(--ink-faint);
}
.cta-dark {
	background: var(--ink);
	border-radius: 15px;
	padding: 16px;
	color: #fff;
}
.cta-dark__title {
	font-family: var(--font-display);
	font-weight: 600;
	font-size: 15px;
}
.cta-dark p {
	font-size: 13px;
	line-height: 1.45;
	color: #aeb4bc;
	margin-top: 5px;
}
.cta-dark button {
	margin-top: 12px;
	background: var(--accent);
	color: #fff;
	border: none;
	cursor: pointer;
	font-size: 13.5px;
	font-weight: 600;
	padding: 9px 14px;
	border-radius: 9px;
}

.ad-slot {
	margin-top: 14px;
	border: 1px dashed #d8d6cd;
	border-radius: 12px;
	height: 90px;
	display: grid;
	place-items: center;
	color: #b4b2a8;
	font-family: var(--font-mono);
	font-size: 11px;
	letter-spacing: 0.1em;
}

/* ---------------- Filter chips + sort ---------------- */
.chips {
	display: flex;
	gap: 7px;
	overflow-x: auto;
	padding-bottom: 4px;
	margin-bottom: 10px;
	-webkit-overflow-scrolling: touch;
}
.chip {
	flex-shrink: 0;
	cursor: pointer;
	font-size: 13px;
	font-weight: 600;
	padding: 7px 12px;
	border-radius: var(--r-pill);
	border: 1px solid var(--hairline);
	background: var(--surface);
	color: var(--ink-2);
}
.chip.is-active {
	background: var(--accent);
	color: #fff;
	border-color: var(--accent);
}

/* The moderator's four main jumping-off points (workspace / escalated /
   challenges / history) -- reworked from a row of small text links because
   the workspace in particular is the primary action of the whole page since
   batching landed, not a footnote next to the title. */
.mod-action-row {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin: 4px 0 16px;
}
.mod-action-row .chip {
	font-size: 14px;
	padding: 9px 16px;
}
.chip--primary {
	background: var(--accent);
	color: #fff;
	border-color: var(--accent);
	font-weight: 700;
}
.chip--primary:hover {
	background: var(--accent-hover);
	border-color: var(--accent-hover);
	color: #fff;
}

/* Compact bar above the list: shows the active filter (or "All") and a
   round button that opens the full type-filter modal -- the always-visible
   grouped grid moved into that modal (below) so the list page itself stays
   short regardless of how many types the row would otherwise take. */
.type-filter-trigger {
	display: flex;
	align-items: center;
	gap: 8px;
	margin-bottom: 14px;
}
.type-filter-current {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	height: 38px;
	padding: 0 12px;
	border: 1px solid var(--hairline);
	border-radius: 10px;
	background: var(--surface);
	cursor: pointer;
	font-size: 13px;
	font-weight: 600;
	color: var(--ink-2);
}
.type-filter-current img {
	width: 20px;
	height: 20px;
	object-fit: contain;
}
.type-filter-current__clear {
	color: var(--ink-faint);
	font-size: 15px;
	line-height: 1;
}
.type-filter-open-btn {
	display: grid;
	place-items: center;
	width: 38px;
	height: 38px;
	flex-shrink: 0;
	border: 1px solid var(--hairline);
	border-radius: 50%;
	background: var(--surface);
	color: var(--accent-deep);
	cursor: pointer;
	justify-content: center;
}
.type-filter-open-btn:hover {
	background: var(--accent-tint);
}

.modal__body > .type-filter-btn {
	margin-bottom: 12px;
}

/* Each labeled group sizes to its own content and sits side-by-side with
   its neighbors, wrapping to a new line only when a row runs out of
   horizontal room -- on desktop that's usually 2-3 groups per line instead
   of one full-width row per group; on narrow mobile it degenerates to the
   same one-group-per-line stack as before. */
.type-filter-group-row {
	display: flex;
	flex-wrap: wrap;
	gap: 4px 20px;
}
/* display:flex beats the UA [hidden] default (author rule wins, same
   specificity) -- same fix as .sec-body[hidden]/.modal[hidden] elsewhere.
   Needed for #items-tag-panel, toggled via data-toggle in app.js. */
.type-filter-group-row[hidden] {
	display: none;
}
.type-filter-group {
	flex: 0 1 auto;
	min-width: 0;
	max-width: 100%;
}
.type-filter-group__label {
	font-size: 12px;
	font-weight: 700;
	color: var(--ink-muted-2);
	text-transform: uppercase;
	letter-spacing: 0.03em;
	margin-bottom: 6px;
}
.type-icons {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
}
/* Named type-filter-btn, not type-icon -- .type-icon is already a
   pre-existing class on the per-card equipment-type <img> in item cards,
   search rows, and the calculator; reusing it here made catalog.js's
   selectors match those unrelated images too. */
.type-filter-btn {
	display: grid;
	place-items: center;
	width: 38px;
	height: 38px;
	flex-shrink: 0;
	border: 1px solid var(--hairline);
	border-radius: 10px;
	background: var(--surface);
	cursor: pointer;
}
.type-filter-btn img {
	width: 22px;
	height: 22px;
	object-fit: contain;
}
/* Iconless entries (text pill) need room for their label instead of the
   fixed icon-square size, or long labels like "Refinement Support" clip. */
.type-filter-btn:has(.type-filter-btn__txt) {
	width: fit-content;
	min-height: 38px;
	padding: 4px 8px;
}
.type-filter-btn.is-active {
	border-color: var(--accent);
	background: var(--accent-tint);
}
.type-filter-btn__txt {
	font-size: 9px;
	font-weight: 700;
	text-align: center;
	line-height: 1.15;
	padding: 0 2px;
	color: var(--ink-2);
}

.stat-filter {
	margin: 8px 0 14px;
	padding: 10px;
	border: 1px solid var(--hairline);
	border-radius: 12px;
	background: var(--surface-sunken);
}
.stat-filter__row {
	display: flex;
	gap: 6px;
	align-items: center;
	margin-bottom: 6px;
}
.stat-filter__row input[type="text"] {
	flex: 1;
	min-width: 0;
	height: 32px;
	border: 1px solid var(--hairline);
	border-radius: 8px;
	padding: 0 8px;
	font-size: 13px;
	background: var(--surface);
}
.stat-filter__row select {
	height: 32px;
	border: 1px solid var(--hairline);
	border-radius: 8px;
	padding: 0 6px;
	font-size: 13px;
	background: var(--surface);
}
.stat-filter__row input[type="number"] {
	width: 90px;
	height: 32px;
	border: 1px solid var(--hairline);
	border-radius: 8px;
	padding: 0 8px;
	font-size: 13px;
	background: var(--surface);
}
.stat-filter__row button {
	flex-shrink: 0;
	border: none;
	background: none;
	color: var(--ink-faint);
	cursor: pointer;
	font-size: 15px;
	padding: 0 4px;
}
.stat-filter__actions {
	display: flex;
	justify-content: space-between;
	margin-top: 4px;
}

.el-chips {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin-bottom: 14px;
}
.el-chip,
.tier-chip {
	cursor: pointer;
	font-size: 13px;
	font-weight: 600;
	padding: 7px 12px;
	border-radius: var(--r-pill);
	border: 1px solid var(--hairline);
	background: var(--surface);
	color: var(--ink-2);
}
.el-chip.is-active,
.tier-chip.is-active {
	background: var(--tile-bg, var(--accent));
	color: #fff;
	border-color: transparent;
}
.sort {
	display: flex;
	align-items: center;
	gap: 8px;
	margin-bottom: 14px;
}
.sort__label {
	font-size: 12.5px;
	font-weight: 600;
	color: var(--ink-muted-2);
	flex-shrink: 0;
}
.sort select {
	flex: 1;
	max-width: 260px;
	height: 34px;
	border: 1px solid var(--hairline);
	border-radius: 9px;
	padding: 0 10px;
	font-size: 13px;
	background: var(--surface);
	color: var(--ink);
	outline: none;
	font-weight: 600;
}

/* ---------------- Card grid + cards ---------------- */
.card-grid {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 12px;
}
/* Infinite scroll (catalog.js): [data-chunks] stacks chunks of 72 cards, each
   its own .card-grid. Column gap mirrors the grid gap so consecutive chunks
   read as one continuous grid. content-visibility lets the browser skip
   layout + paint for off-screen chunks — the main cost saver on weak CPUs;
   the intrinsic-size estimate only matters before a chunk's first paint. */
[data-chunks] {
	display: flex;
	flex-direction: column;
	gap: 12px;
}
.card-grid[data-chunk] {
	content-visibility: auto;
	contain-intrinsic-size: auto 4000px;
}
.list-end {
	padding: 18px 0 6px;
	text-align: center;
}
.card {
	background: var(--surface);
	border: 1px solid transparent;
	border-radius: 20px;
	overflow: hidden;
	display: flex;
	flex-direction: column;
	position: relative;
	box-shadow:
		0 1px 2px rgba(27, 34, 43, 0.04),
		0 12px 26px -16px rgba(181, 115, 78, 0.16);
	transition:
		transform 0.28s cubic-bezier(0.2, 0.7, 0.2, 1),
		box-shadow 0.28s cubic-bezier(0.2, 0.7, 0.2, 1);
}
/* z-index on hover: without it, the glow bleeding below the card is painted
   over by the next grid row's cards (later in DOM = higher default paint
   order in the same stacking context), which visually clips the bottom of
   the transition. Lifting the hovered card above its siblings fixes it. */
.card:hover {
	transform: translateY(-4px);
	box-shadow:
		0 2px 4px rgba(27, 34, 43, 0.06),
		0 22px 40px -16px rgba(181, 115, 78, 0.28);
	z-index: 2;
}
.card__head {
	display: flex;
	align-items: center;
	gap: 8px;
	width: 100%;
	text-align: left;
	background: none;
	border: none;
	cursor: pointer;
	padding: 14px 14px 11px 16px;
	text-decoration: none;
}
.card__name {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 15px;
	color: var(--ink);
	letter-spacing: -0.01em;
	line-height: 1.2;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.card__type {
	font-size: 11px;
	font-weight: 700;
	color: var(--material-ink);
	flex-shrink: 0;
}
.card__body {
	padding: 4px 16px 6px;
}

.thumb {
	display: block;
	width: 100%;
	border: none;
	background: none;
	padding: 10px 12px;
	cursor: zoom-in;
	box-shadow: none;
}
.thumb__img {
	display: block;
	width: 100%;
	border-radius: 16px;
	position: relative;
	overflow: hidden;
	background-image: linear-gradient(
		135deg,
		rgba(255, 255, 255, 0.55) 25%,
		transparent 25%,
		transparent 50%,
		rgba(255, 255, 255, 0.55) 50%,
		rgba(255, 255, 255, 0.55) 75%,
		transparent 75%
	);
	background-size: 13px 13px;
}
.thumb__img--item {
	aspect-ratio: 15 / 18;
}
.thumb__img--mon {
	aspect-ratio: 15 / 12;
	background-size: 15px 15px;
}
.thumb__img img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: contain;
}
.thumb__id {
	position: absolute;
	left: 8px;
	bottom: 8px;
	font-family: var(--font-mono);
	font-size: 9px;
	font-weight: 700;
	background: #fff;
	padding: 2px 6px;
	border-radius: 20px;
}

/* Equipment-type icons (35px source pixel art, catalog.constants.ICON_BY_ITEM_TYPE).
   Centred at a fixed multiple of their native size rather than stretched to fill
   the tile -- upscaling to a 200px card thumb would blur them, and `pixelated`
   keeps the edges crisp at the 2x/3x steps below. The `position: static` resets
   are deliberate: they beat the fill-the-tile `.thumb__img img` / `.media-card__img
   img` rules that real per-item art will still use. */
.type-icon {
	image-rendering: pixelated;
}
.thumb__img .type-icon {
	position: absolute;
	inset: 0;
	margin: auto;
	width: 70px;
	height: 70px;
	object-fit: contain;
}
.media-card__img .type-icon {
	position: static;
	width: 105px;
	height: 105px;
	max-width: 60%;
	max-height: 60%;
	object-fit: contain;
}
.tile-sm .type-icon {
	width: 26px;
	height: 26px;
	object-fit: contain;
}
.thumb__cap {
	position: absolute;
	left: 8px;
	bottom: 8px;
	font-family: var(--font-mono);
	font-size: 9.5px;
	font-weight: 700;
	color: #fff;
	background: rgba(0, 0, 0, 0.24);
	padding: 3px 8px;
	border-radius: 20px;
}
.thumb__zoom {
	position: absolute;
	right: 8px;
	top: 8px;
	width: 22px;
	height: 22px;
	border-radius: 8px;
	background: rgba(255, 255, 255, 0.92);
	display: grid;
	place-items: center;
}
.thumb__gallery {
	position: absolute;
	right: 8px;
	bottom: 8px;
	display: flex;
	align-items: center;
	gap: 3px;
	font-family: var(--font-mono);
	font-size: 9px;
	font-weight: 700;
	color: #2c7cb6;
	background: rgba(255, 255, 255, 0.92);
	padding: 3px 6px;
	border-radius: 20px;
}

/* collapsible section pills (revises the old flat accent/tint bars) */
.sec-bar {
	width: 100%;
	display: flex;
	align-items: center;
	justify-content: space-between;
	border: none;
	cursor: pointer;
	font: inherit;
	padding: 9px 13px;
	border-radius: 13px;
	margin-top: 8px;
	transition:
		background 0.2s,
		color 0.2s;
	box-shadow: none;
}
.sec-bar:first-child,
.card__body > .sec-bar:first-of-type {
	margin-top: 0;
}
.sec-bar__label {
	font-size: 12px;
	font-weight: 700;
}
.sec-bar__chev {
	width: 15px;
	height: 15px;
	flex-shrink: 0;
	transition: transform 0.3s cubic-bezier(0.2, 0.7, 0.2, 1);
}
/* State comes from aria-expanded (JS keeps it in sync); the --open/--closed
   classes are kept as aliases so first paint and no-JS still colour correctly. */
.sec-bar[aria-expanded="true"],
.sec-bar--open {
	background: var(--accent-tint);
	color: var(--accent-deep);
}
.sec-bar[aria-expanded="true"] .sec-bar__chev {
	transform: rotate(180deg);
}
.sec-bar[aria-expanded="false"],
.sec-bar--closed {
	background: var(--surface-sunken);
	color: var(--ink-2);
}
.sec-bar[aria-expanded="false"]:hover,
.sec-bar--closed:hover {
	background: var(--hairline-soft);
}
.sec-bar--open .sec-bar__label,
.sec-bar--closed .sec-bar__label {
	color: inherit;
}
.sec-bar__sign {
	display: none;
} /* replaced by the chevron */
.sec-body {
	padding: 6px 4px 6px;
}
.sec-body[hidden] {
	display: none;
}
.sec-body .drop-list .drop-row {
	animation: rowIn 0.38s both;
}
@keyframes rowIn {
	from {
		opacity: 0;
		transform: translateY(4px);
	}
	to {
		opacity: 1;
		transform: none;
	}
}

/* underline tabs (item/monster card sections -- replaces per-section accordion
   with a single-panel-at-a-time view so the card doesn't grow with every
   section left open). Prefixed card-tab* -- a bare .tabs/.tab already exists
   further down (dead/orphaned, unused by any template) and collides. */
.card-tab-bar {
	display: flex;
	gap: 16px;
	border-bottom: 1px solid var(--hairline-soft);
}
.card__body > .card-tab-bar {
	margin-top: 0;
}
.card-tab {
	position: relative;
	background: none;
	border: none;
	box-shadow: none;
	cursor: pointer;
	font: inherit;
	padding: 4px;
	font-size: 12px;
	font-weight: 700;
	color: var(--ink-faint);
	transition: color 0.16s;
	outline: none;
	-webkit-tap-highlight-color: transparent;
}
.card-tab:hover {
	color: var(--ink-2);
}
.card-tab:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 2px;
	border-radius: 4px;
}
.card-tab[aria-selected="true"] {
	color: var(--accent-deep);
}
.card-tab[aria-selected="true"]::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: -1px;
	height: 2px;
	border-radius: 2px;
	background: var(--accent-deep);
}
.card-tab-panel {
	padding: 8px 4px 6px;
}
.card-tab-panel[hidden] {
	display: none;
}
.card-tab-panel .drop-list .drop-row {
	animation: rowIn 0.38s both;
}

.stat-row {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 8px;
	padding: 6px 0;
	border-bottom: 1px solid var(--hairline-softer);
}
/* .stat-row/.stat-cond set display:flex/block, which otherwise beats the UA
   [hidden] default (author rule wins regardless of specificity) -- same fix
   as .drop-row[hidden]/.source[hidden] above, needed for the stat-list pager. */
.stat-row[hidden],
.stat-cond[hidden] {
	display: none;
}
.stat-row__label {
	font-size: 12.5px;
	color: var(--ink-2);
	font-weight: 500;
}
.stat-row__value {
	font-family: var(--font-mono);
	font-weight: 700;
	font-size: 12.5px;
	text-align: right;
	color: var(--accent-deep);
}
.v-neg {
	color: var(--danger-ink);
}
.v-sell {
	color: var(--success-2);
}
.v-material {
	color: var(--material-ink);
}

/* obtained-from grid */
/* grid-template-columns covers the 3-header case (Field/Before/After,
   Item/·/Amount) as well as the 2-header case (Monster/Map) -- an unused
   3rd track on the 2-header callers has no content to place in it, so it
   stays zero-width and doesn't affect their layout. */
.sources-head {
	display: grid;
	grid-template-columns: 1fr auto auto;
	gap: 3px 8px;
	font-size: 10px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	color: var(--ink-faint);
	padding: 0 0 4px;
	border-bottom: 1px solid var(--hairline-soft);
}
.source {
	display: grid;
	grid-template-columns: 1fr auto;
	gap: 2px 8px;
	align-items: center;
	width: 100%;
	background: none;
	border: none;
	cursor: pointer;
	text-align: left;
	padding: 6px 0;
	border-bottom: 1px solid var(--hairline-softer);
}
.source__name {
	font-size: 12.5px;
	font-weight: 600;
	color: var(--accent-deep);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.source__map {
	font-size: 12px;
	font-weight: 600;
	color: var(--material-ink);
	text-align: right;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.source__note {
	font-size: 10.5px;
	color: var(--ink-faint);
}
.source__chance {
	font-family: var(--font-mono);
	font-weight: 700;
	font-size: 11px;
	color: var(--success-2);
	text-align: right;
}

/* monster drops */
.drop-row {
	display: flex;
	align-items: center;
	gap: 9px;
	background: none;
	border: none;
	cursor: pointer;
	text-align: left;
	padding: 6px 8px;
	width: 100%;
	border-radius: 10px;
	text-decoration: none;
	transition: background 0.16s;
}
.drop-row:hover {
	background: #fbf7f2;
}
.drop-list {
	padding: 4px 0;
	display: flex;
	flex-direction: column;
	gap: 2px;
}
.tile-mini {
	width: 24px;
	height: 24px;
	border-radius: 8px;
	display: grid;
	place-items: center;
	font-family: var(--font-mono);
	font-weight: 700;
	font-size: 8px;
	flex-shrink: 0;
}
.drop-row__name {
	flex: 1;
	min-width: 0;
	font-size: 12.5px;
	font-weight: 600;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.drop-row__chance {
	font-family: var(--font-mono);
	font-weight: 700;
	font-size: 11.5px;
	color: var(--success-2);
	flex-shrink: 0;
}
.map-row {
	padding: 8px 8px 4px;
	display: flex;
	align-items: center;
	gap: 9px;
}
.map-row span {
	font-size: 12.5px;
	font-weight: 600;
	color: var(--ink-2);
}

/* card lists paginated to a uniform 5 rows (see app.js initPagedLists) --
   .is-filler rows are clones of the last real row, kept in the DOM (same
   markup/height as a real row) but invisible, so short lists still occupy
   5 rows of vertical space and every card in a grid ends up the same height. */
.drop-row[hidden],
.source[hidden] {
	display: none;
}
.is-filler {
	visibility: hidden;
	pointer-events: none;
}
.list-pager {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 10px;
	padding: 4px 0 2px;
}
.list-pager__btn {
	background: none;
	border: none;
	padding: 0;
	width: 22px;
	height: 22px;
	border-radius: 999px;
	display: grid;
	place-items: center;
	cursor: pointer;
	color: var(--ink-muted);
}
.list-pager__btn:hover:not(:disabled) {
	color: var(--accent-deep);
}
.list-pager__btn:disabled {
	opacity: 0.3;
	cursor: default;
}
.list-pager__pos {
	font-family: var(--font-mono);
	font-size: 11px;
	color: var(--ink-faint);
}

.mode-badge {
	font-size: 10px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	padding: 3px 9px;
	border-radius: 20px;
	flex-shrink: 0;
}

/* ---- social action bar ---- */
/* margin-top: auto (not 10px) so it's pinned to the card's bottom edge
   instead of trailing right after content -- .card is a flex column, so this
   pushes .actions down regardless of how much the sections above it (varying
   drop-list lengths, etc.) fill, keeping every card in a row's action bar on
   the same line. */
.actions {
	display: flex;
	align-items: center;
	gap: 2px;
	margin-top: auto;
	padding: 8px 8px 10px;
	border-top: 1px solid var(--hairline-soft);
}
.act {
	flex: 1;
	display: inline-flex;
	flex-direction: column;
	align-items: center;
	gap: 3px;
	border: none;
	background: none;
	font: inherit;
	color: var(--ink-muted);
	cursor: pointer;
	padding: 7px 4px;
	border-radius: 12px;
	text-decoration: none;
	position: relative;
	transition:
		background 0.18s,
		color 0.18s;
	box-shadow: none;
}
.act:hover {
	background: var(--surface-sunken);
	color: var(--ink-2);
}
.act:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 2px;
}
.act .icwrap {
	position: relative;
	display: grid;
	place-items: center;
	height: 19px;
}
.act svg {
	width: 19px;
	height: 19px;
}
.act__label {
	font-size: 10px;
	font-weight: 700;
	letter-spacing: 0.02em;
}
.act__count {
	font-family: var(--font-mono);
	font-weight: 700;
}
.act--comment:hover {
	color: var(--accent-deep);
}
.act--edit:hover {
	color: var(--material-ink);
}
.act--share:hover {
	color: var(--success-2);
}
.act--save:hover {
	color: #d98a5b;
}
.act--save.on {
	color: #d98a5b;
}
.act--save.on .bk {
	fill: #d98a5b;
	stroke: #d98a5b;
}
.act--save.pop svg {
	animation: favPop 0.42s cubic-bezier(0.2, 1.4, 0.4, 1);
}
@keyframes favPop {
	0% {
		transform: scale(1);
	}
	35% {
		transform: scale(1.35);
	}
	60% {
		transform: scale(0.9);
	}
	100% {
		transform: scale(1);
	}
}
.spark {
	position: absolute;
	top: 6px;
	left: 50%;
	width: 4px;
	height: 4px;
	border-radius: 50%;
	background: #e0a86a;
	opacity: 0;
	pointer-events: none;
}
.act--save.pop .spark {
	animation: favSpark 0.55s ease-out;
}
@keyframes favSpark {
	0% {
		opacity: 1;
		transform: translate(-50%, 0) scale(1);
	}
	100% {
		opacity: 0;
		transform: translate(-50%, -16px) scale(0.3);
	}
}
.act--share .ic {
	transition: opacity 0.18s;
}
.act--share .check {
	opacity: 0;
	position: absolute;
	inset: 0;
	display: grid;
	place-items: center;
}
.act--share.done .ic {
	opacity: 0;
}
.act--share.done .check {
	opacity: 1;
	animation: favPop 0.42s cubic-bezier(0.2, 1.4, 0.4, 1);
}

.section-title {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 16px;
	margin: 22px 0 10px;
}

@media (prefers-reduced-motion: reduce) {
	.card,
	.sec-bar__chev,
	.act,
	.drop-row,
	.act--save.pop svg,
	.act--save.pop .spark,
	.act--share.done .check {
		transition-duration: 0.01ms !important;
		animation-duration: 0.01ms !important;
		animation: none !important;
	}
	.card:hover {
		transform: none;
	}
}

/* ---------------- Search results ---------------- */
.result-label {
	font-size: 12px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--ink-faint);
	margin: 0 0 8px 2px;
}
.list-card {
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: var(--r-card);
	overflow: hidden;
	margin-bottom: 16px;
}
.list-row {
	width: 100%;
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 10px 12px;
	background: none;
	border: none;
	border-top: 1px solid var(--hairline-soft);
	cursor: pointer;
	text-align: left;
	color: inherit;
}
.list-row:first-child {
	border-top: none;
}
.list-row:hover {
	background: var(--row-hover);
}
.tile-sm {
	width: 34px;
	height: 34px;
	border-radius: 9px;
	display: grid;
	place-items: center;
	font-family: var(--font-mono);
	font-weight: 700;
	font-size: 11px;
	flex-shrink: 0;
}
.list-row__name {
	display: block;
	font-size: 14px;
	font-weight: 600;
	color: var(--ink);
}
.list-row__sub {
	display: block;
	font-size: 12px;
	color: var(--ink-muted-2);
}
.list-row__key {
	font-family: var(--font-mono);
	font-weight: 700;
	font-size: 12.5px;
	color: var(--accent-deep);
	flex-shrink: 0;
}
.list-row__link {
	flex: 1;
	min-width: 0;
	display: block;
	color: inherit;
	text-decoration: none;
}
.empty {
	text-align: center;
	padding: 48px 20px;
	color: var(--ink-faint);
}
.mascot {
	display: block;
	width: 40dvw;
	height: auto;
	margin: 0 auto 14px;
}
.empty__title {
	font-family: var(--font-display);
	font-size: 16px;
	color: var(--ink-muted);
}
.empty p {
	font-size: 13px;
	margin-top: 6px;
}
.link-btn {
	background: none;
	border: none;
	color: var(--accent-deep);
	font-weight: 600;
	cursor: pointer;
	font-size: 13px;
	padding: 0;
}

/* ---------------- Detail ---------------- */
.back {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	background: none;
	border: none;
	cursor: pointer;
	font-size: 13px;
	font-weight: 600;
	color: var(--ink-muted-2);
	padding: 0;
	margin-bottom: 14px;
}
.detail {
	display: grid;
	grid-template-columns: 1fr;
	gap: 16px;
}
.media-card {
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: 16px;
	padding: 16px;
}
.media-card__img {
	aspect-ratio: 1;
	border-radius: 12px;
	display: grid;
	place-items: center;
	position: relative;
	overflow: hidden;
	background-image: linear-gradient(
		135deg,
		rgba(255, 255, 255, 0.55) 25%,
		transparent 25%,
		transparent 50%,
		rgba(255, 255, 255, 0.55) 50%,
		rgba(255, 255, 255, 0.55) 75%,
		transparent 75%
	);
	background-size: 14px 14px;
}
.media-card__img img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: contain;
}
.chip-row {
	display: flex;
	flex-wrap: wrap;
	gap: 5px;
	margin-top: 12px;
}
.meta-chip {
	font-size: 11px;
	font-weight: 600;
	color: var(--ink-muted-2);
	background: var(--hairline-soft);
	padding: 3px 8px;
	border-radius: 6px;
}
.meta-chip--type {
	font-weight: 700;
}
.detail__title {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 26px;
	letter-spacing: -0.02em;
	color: var(--ink);
	line-height: 1.05;
}
.detail__sub {
	font-size: 13.5px;
	color: var(--ink-muted-2);
	margin-top: 4px;
}
/* The "N of M" middle child is .field__hint, which is display:block (needed
   elsewhere for hint text under an input) -- inside a plain block .detail__sub
   that forces a line break before AND after it, stacking Previous / "N of M" /
   Next onto three separate lines. A flex container makes every direct child a
   flex item regardless of its own display, so this overrides that cleanly. */
.workspace-pager {
	display: flex;
	align-items: center;
	gap: 10px;
	flex-wrap: wrap;
}
.provenance {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	margin-top: 10px;
	font-size: 12px;
	font-weight: 600;
	color: #5f87a8;
	background: var(--accent-tint-2);
	border: 1px solid var(--accent-tint-border-3);
	padding: 4px 9px;
	border-radius: 8px;
}
.upgrade-chain {
	margin-top: 8px;
	display: flex;
	flex-direction: column;
	gap: 2px;
	font-size: 12.5px;
	color: var(--ink-muted-2);
}
.upgrade-chain a {
	font-weight: 600;
}
.stat-block {
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: 14px;
	padding: 6px 14px;
	margin-top: 14px;
}
.stat-block__row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 9px 0;
	border-top: 1px solid var(--hairline-softer);
}
.stat-block__row:first-child {
	border-top: none;
}
.stat-block__label {
	font-size: 13.5px;
	color: var(--ink-2);
	font-weight: 500;
}
.stat-block__value {
	font-family: var(--font-mono);
	font-weight: 700;
	font-size: 13.5px;
	color: var(--accent-deep);
}
.sub-head {
	font-family: var(--font-display);
	font-weight: 600;
	font-size: 15px;
	margin: 18px 0 9px;
}
.stat-cond {
	margin-top: 4px;
	padding-left: 12px;
	border-left: 2px solid var(--hairline);
}
.stat-cond--compact {
	padding-left: 8px;
}
.stat-cond__head {
	font-size: 12px;
	font-weight: 600;
	color: var(--ink-faint);
	margin: 10px 0 2px;
}
.stat-block__row--cond,
.stat-row--cond {
	opacity: 0.95;
}
.source-card {
	display: flex;
	align-items: center;
	gap: 11px;
	padding: 10px 12px;
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: 11px;
	cursor: pointer;
	text-align: left;
	color: inherit;
	width: 100%;
}
.source-card:hover {
	border-color: var(--accent-tint-border);
}
.source-card__name {
	display: block;
	font-size: 13.5px;
	font-weight: 600;
	color: var(--ink);
}
.source-card__note {
	display: block;
	font-size: 12px;
	color: var(--ink-faint);
}
.source-card__chance {
	font-family: var(--font-mono);
	font-weight: 700;
	font-size: 12.5px;
	color: var(--accent-deep);
}
.stack-8 {
	display: flex;
	flex-direction: column;
	gap: 8px;
}
.mon-stat-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
	gap: 8px;
	margin-top: 14px;
}
.mon-stat {
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: 11px;
	padding: 10px 11px;
}
.mon-stat__label {
	font-size: 11px;
	font-weight: 600;
	color: var(--ink-faint);
	text-transform: uppercase;
	letter-spacing: 0.03em;
}
.mon-stat__value {
	font-family: var(--font-mono);
	font-weight: 700;
	font-size: 15px;
	color: var(--ink);
	margin-top: 3px;
}
.art-card {
	border-radius: 16px;
	overflow: hidden;
	border: 1px solid var(--hairline);
}
.art-card__img {
	aspect-ratio: 4 / 3;
	background-size: 16px 16px;
	display: grid;
	place-items: center;
	position: relative;
}
.art-card__img img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: contain;
}

/* ---------------- Contribute ---------------- */
.narrow {
	max-width: 720px;
	margin: 0 auto;
}
.lede {
	font-size: 14px;
	color: var(--ink-muted);
	margin-top: 5px;
	line-height: 1.5;
}
.trust-card {
	display: flex;
	align-items: center;
	gap: 13px;
	background: linear-gradient(135deg, var(--accent-tint-3), var(--canvas));
	border: 1px solid var(--accent-tint-border-2);
	border-radius: 14px;
	padding: 14px 15px;
	margin-top: 16px;
}
.trust-avatar {
	width: 42px;
	height: 42px;
	border-radius: 12px;
	background: var(--accent);
	display: grid;
	place-items: center;
	font-family: var(--font-display);
	font-weight: 700;
	color: #fff;
	font-size: 18px;
	flex-shrink: 0;
}
.trust-badge {
	font-size: 10.5px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--accent-deep);
	background: #fff;
	border: 1px solid var(--accent-tint-border);
	padding: 2px 7px;
	border-radius: 6px;
}
.tabs {
	display: flex;
	gap: 4px;
	margin-top: 16px;
	border-bottom: 1px solid var(--hairline);
}
.tab {
	background: none;
	border: none;
	cursor: pointer;
	font-size: 13.5px;
	font-weight: 600;
	padding: 9px 12px;
	color: var(--ink-muted-2);
	border-bottom: 2px solid transparent;
	margin-bottom: -1px;
	text-decoration: none;
}
.tab.is-active {
	color: var(--ink);
	border-bottom-color: var(--accent);
}
.form-card {
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: 14px;
	padding: 16px;
	margin-top: 16px;
}
.form-grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: 12px;
}
.field {
	display: block;
}
.field > span {
	display: block;
	font-size: 12.5px;
	font-weight: 600;
	color: var(--ink-2);
	margin-bottom: 5px;
}
.field select,
.field input,
.field textarea {
	width: 100%;
	border: 1px solid var(--hairline);
	border-radius: 9px;
	font-size: 14px;
	outline: none;
	color: var(--ink);
	background: var(--surface);
}
.field select,
.field input {
	height: 40px;
	padding: 0 10px;
}
.field textarea {
	min-height: 96px;
	resize: vertical;
	padding: 10px;
	line-height: 1.45;
}
.dropzone {
	position: relative;
	border: 1.5px dashed var(--accent-tint-border);
	border-radius: 11px;
	background: var(--row-hover);
	padding: 18px;
	text-align: center;
	cursor: pointer;
}
.dropzone.is-dragover {
	border-color: var(--accent);
	background: var(--accent-tint);
}
.dropzone input[type="file"] {
	position: absolute;
	inset: 0;
	opacity: 0;
	cursor: pointer;
	margin: 0;
}
.dropzone input[type="file"]:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 2px;
	border-radius: inherit;
}
.dropzone__title {
	font-size: 13px;
	font-weight: 600;
	color: var(--accent-deep);
	margin-top: 6px;
}
.dropzone__link {
	text-decoration: underline;
}
.dropzone__note {
	font-size: 11.5px;
	color: var(--ink-faint);
	margin-top: 2px;
}

.dropzone__staged {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin-top: 8px;
}
.dropzone__preview {
	position: relative;
	width: 56px;
	flex-shrink: 0;
	border-radius: 8px;
	overflow: hidden;
	background: var(--surface-alt);
	border: 1px solid var(--hairline);
}
.dropzone__preview img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}
.dropzone__preview-remove {
	position: absolute;
	top: 2px;
	right: 2px;
	width: 17px;
	height: 17px;
	border-radius: 999px;
	border: 0;
	background: rgba(27, 34, 43, 0.66);
	color: var(--surface);
	font-size: 11px;
	line-height: 1;
	cursor: pointer;
	padding: 0;
}
.dropzone__preview-remove:hover {
	background: rgba(27, 34, 43, 0.85);
}

.dye-row {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 8px;
}
.dye-row__slot {
	display: block;
	position: relative;
}
.dye-row__tag {
	position: absolute;
	left: 10px;
	top: -7px;
	z-index: 1;
	padding: 0 4px;
	background: var(--surface);
	font-size: 10px;
	font-weight: 700;
	color: var(--ink-faint);
}
.honeypot {
	position: absolute;
	left: -9999px;
	opacity: 0;
}
.form-foot {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	margin-top: 16px;
	flex-wrap: wrap;
}
.form-foot__note {
	font-size: 12px;
	color: var(--ink-faint);
	display: flex;
	align-items: center;
	gap: 6px;
}
.btn-primary {
	background: var(--accent);
	color: #fff;
	border: none;
	cursor: pointer;
	font-size: 14px;
	font-weight: 600;
	padding: 10px 18px;
	border-radius: 10px;
}
.sub-row {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 12px;
	border-top: 1px solid var(--hairline-soft);
}
.sub-row:first-child {
	border-top: none;
}
.status-chip {
	font-size: 11px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	padding: 3px 8px;
	border-radius: 6px;
	flex-shrink: 0;
}
.status--pending {
	color: var(--warn-ink);
	background: var(--warn-bg);
}
.status--approved {
	color: var(--success-2);
	background: #ecf7f0;
}
.status--rejected {
	color: var(--danger-ink);
	background: var(--danger-bg);
}
.status--escalated {
	color: var(--g-special-ink);
	background: var(--g-special-bg);
}
.status--challenged {
	color: var(--warn-ink);
	background: var(--warn-bg);
}
/* The remaining lifecycle states. Every state the Draft Space renders needs a
   modifier: .status-chip sets no colour of its own, so a missing one renders
   as bare uppercase text next to properly-pilled siblings. Tinted neutrals
   from DESIGN.md -- deliberately quieter than approved/rejected, because most
   of these are "in progress", not a verdict. */
.status--draft {
	color: var(--ink-muted);
	background: var(--surface-alt);
}
.status--ready {
	color: var(--accent-deep);
	background: var(--accent-tint);
}
.status--queued {
	color: var(--warn-ink);
	background: var(--warn-bg);
}
.status--on_review {
	color: var(--accent-hover);
	background: var(--accent-tint-2);
}
.status--change_requested {
	color: var(--material-ink);
	background: var(--material-bg);
}
.sweep-banner {
	background: var(--warn-bg);
	color: var(--warn-ink);
	margin-bottom: 12px;
}

/* ---- Contribution history cards (moderation history + public profile) ----
   Every card is a fixed height regardless of how much a contribution's diff/
   note/photo actually holds -- long content is clipped by .contrib-card__preview's
   overflow:hidden (a partially-cut row is a fine "there's more" affordance) and
   a "+N more" line where truncated. The full content lives in the modal that
   opens on click (data-modal-open, wired up by app.js's existing generic modal
   handler -- no new JS needed). */
/* Column count is content-driven, not breakpoint-driven: one column on a
   phone (two columns there left ~160px cards whose titles ellipsised down to
   a few characters), two from ~560px up. The track floor is min(100%, 260px),
   not a bare 260px, so a viewport narrower than the floor still resolves to
   one column that fits instead of overflowing.
   A bare `1fr` was the bug behind the zoomed-out Draft tab on phones: an fr
   track's automatic minimum is the item's min-content, and the card's nowrap
   title + status chip put that near 280px, so two columns forced the
   *document* ~640px wide and the browser shrank the whole page to fit. Only
   the Draft tab showed it because .card's overflow:hidden zeroes that
   automatic minimum -- on Draft the grid item is the checkbox wrapper around
   the card, which has no such clip. Hence min-width:0 on both, plus the
   explicit track floor. */
.contrib-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr));
	gap: 12px;
}
@media (min-width: 820px) {
	.contrib-grid {
		grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
	}
}
.contrib-card {
	width: 100%;
	min-width: 0;
	height: 220px;
	padding: 14px 16px;
	text-align: left;
	font: inherit;
	color: inherit;
	border: 1px solid transparent;
	cursor: pointer;
	appearance: none;
	display: flex;
	flex-direction: column;
	gap: 6px;
}
.contrib-card__top {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
}
.contrib-card__title {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 14.5px;
	color: var(--ink);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	min-width: 0;
}
.contrib-card__meta {
	font-size: 11.5px;
	color: var(--ink-faint);
}
.contrib-card__preview {
	flex: 1;
	min-height: 0;
	overflow: hidden;
	display: flex;
	flex-direction: column;
	gap: 2px;
	margin-top: 2px;
}
.contrib-card__more {
	font-size: 11px;
	font-weight: 600;
	color: var(--ink-faint);
	margin-top: 2px;
	flex-shrink: 0;
}
.contrib-card__photo {
	border-radius: 10px;
	overflow: hidden;
	background: var(--surface-sunken);
}
.contrib-card__photo img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

/* ---- Draft tab bulk select (profile page) ----
   The checkbox sits beside the card rather than overlaid on top of it: an
   overlay both covered the card's title text and, once .card:hover lifts the
   card to z-index:2 (see the comment above .card:hover), painted over a
   checkbox left at z-index:1 in the same stacking context. Placing it outside
   the card sidesteps both problems instead of chasing z-index values. */
.contrib-card-wrap {
	display: flex;
	align-items: flex-start;
	gap: 8px;
	min-width: 0;
}
.contrib-card-wrap .contrib-card {
	flex: 1;
	min-width: 0;
}
.contrib-card-check {
	flex-shrink: 0;
	margin-top: 14px;
	width: 16px;
	height: 16px;
	cursor: pointer;
}
.bulk-toolbar {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 10px;
	padding: 10px 12px;
	margin-bottom: 12px;
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: 10px;
	font-size: 13px;
	color: var(--ink-2);
}
.bulk-toolbar__all {
	display: flex;
	align-items: center;
	gap: 6px;
	cursor: pointer;
}
.bulk-toolbar__count {
	color: var(--ink-faint);
	margin-right: auto;
}
.chip--danger {
	background: var(--danger-ink);
	color: #fff;
	border-color: var(--danger-ink);
	font-weight: 700;
}
.chip--danger:hover {
	color: #fff;
}

.ladder-item {
	display: flex;
	gap: 12px;
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: 12px;
	padding: 13px 14px;
}
.ladder-item__n {
	font-family: var(--font-mono);
	font-weight: 700;
	font-size: 13px;
	color: var(--accent);
	flex-shrink: 0;
	width: 20px;
}
.ladder-item__title {
	font-size: 14px;
	font-weight: 600;
	color: var(--ink);
}
.ladder-item__desc {
	font-size: 12.5px;
	color: var(--ink-muted-2);
	margin-top: 2px;
	line-height: 1.45;
}

/* ---------------- Modal (lightbox) ---------------- */
.modal {
	position: fixed;
	inset: 0;
	z-index: 80;
	background: rgba(27, 34, 43, 0.62);
	backdrop-filter: blur(3px);
	display: grid;
	place-items: center;
	padding: 24px;
	animation: potFade 0.18s ease both;
}
.modal[hidden] {
	display: none;
}
.modal__panel {
	max-width: min(600px, 92vw);
	max-height: min(88vh, 720px);
	width: 100%;
	background: var(--surface);
	border-radius: 18px;
	overflow: hidden;
	box-shadow: 0 24px 60px rgba(27, 34, 43, 0.4);
	display: flex;
	flex-direction: column;
}
.modal__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	padding: 13px 16px;
	border-bottom: 1px solid var(--hairline-soft);
	flex-shrink: 0;
}
.modal__name {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 16px;
	color: var(--ink);
}
.modal__close {
	background: none;
	border: none;
	cursor: pointer;
	width: 28px;
	height: 28px;
	padding: 16px;
	border-radius: 8px;
	display: grid;
	place-items: center;
	flex-shrink: 0;
	box-shadow: none;
}
.modal__body {
	padding: 16px;
	background: var(--canvas);
	overflow-y: auto;
}
/* Image lightbox (#pot-modal, built by app.js openModal) -- iOS-style:
   the panel shrink-wraps the image so there are no letterbox bands, the
   image runs full-bleed to the panel edge, and the header floats over the
   top as a single translucent glass scrim instead of a solid strip + a
   tinted body band. Scoped to #pot-modal so the shared confirm dialog and
   anchored dropdown menus (which reuse .modal__panel/__head/__body) are
   untouched. */
#pot-modal .modal__panel,
.appearance-slideshow {
	position: relative;
	width: fit-content;
	max-width: 94vw;
	/* drop the base .modal__panel min(88vh,720px) cap: with overflow:hidden it
	   clips wide screenshots that scale past 720px tall. Let the panel grow to
	   the image, which stays self-limited by its own max-height (88vh/74vh). */
	max-height: 94vh;
}
.appearance-slideshow {
	/* the dots/credit/admin rows sit directly on the panel bg below the
	   image (modal__body is transparent), so give the panel the same glass
	   wash as the header instead of the base .modal__panel white surface. */
	background: rgba(20, 26, 33, 0.72);
	-webkit-backdrop-filter: blur(14px) saturate(1.3);
	backdrop-filter: blur(14px) saturate(1.3);
}
#pot-modal .modal__head,
.appearance-slideshow .modal__head {
	position: absolute;
	inset: 0 0 auto 0;
	z-index: 3;
	justify-content: center;
	padding: 11px 16px 22px;
	border-bottom: none;
	/* glass scrim: blur + dark wash fading to clear so the image reads
	   underneath. rgba fallback stays legible where backdrop-filter is
	   unsupported (older low-end Android). */
	background: linear-gradient(
		to bottom,
		rgba(20, 26, 33, 0.55),
		rgba(20, 26, 33, 0)
	);
	-webkit-backdrop-filter: blur(14px) saturate(1.3);
	backdrop-filter: blur(14px) saturate(1.3);
	-webkit-mask-image: linear-gradient(to bottom, #000 55%, transparent);
	mask-image: linear-gradient(to bottom, #000 55%, transparent);
}
#pot-modal .modal__name,
.appearance-slideshow .modal__name {
	color: #fff;
	text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
}
#pot-modal .modal__body {
	padding: 0;
	background: transparent;
	overflow: hidden;
}
#pot-modal .modal__img {
	/* fill flush to the panel's rounded clip; no inner corner ring */
	border-radius: 0;
}
.modal__img {
	/* floor so small transparent catalog icons still get a framed tint;
	   caps so large user photos fit the viewport. Container sizes to the
	   image between these bounds -> no dead bands for wide screenshots. */
	min-width: 240px;
	min-height: 180px;
	max-width: 92vw;
	max-height: 88vh;
	border-radius: 12px;
	display: grid;
	place-items: center;
	position: relative;
	overflow: hidden;
}
.modal__img img {
	display: block;
	width: auto;
	height: auto;
	max-width: 92vw;
	max-height: 88vh;
	object-fit: contain;
}
/* Ratio-locked zoom (item/monster card thumbs, data-zoom-ratio in app.js's
   openModal): a fixed, responsive box instead of sizing to the uploaded
   image's own pixels, same rationale as .appearance-slideshow__frame above.
   Scoped to #pot-modal.modal--zoom-ratio so the avatar zoom (no ratio set)
   keeps its natural min/max-bounded sizing. */
#pot-modal.modal--zoom-ratio .modal__img {
	min-width: 0;
	min-height: 0;
	width: min(75vw, calc(75vh * var(--zoom-ratio)));
	aspect-ratio: var(--zoom-ratio);
}
#pot-modal.modal--zoom-ratio .modal__img img {
	width: 100%;
	height: 100%;
	max-width: none;
	max-height: none;
}

/* Opt-in popover mode (data-modal-anchor on the trigger, see app.js's
   anchorModalTo) -- panel is pinned near its trigger by inline top/left
   instead of grid-centered, and the backdrop stays fully transparent (a
   dropdown shouldn't dim the whole page) while still catching outside
   clicks to close, same as the centered modal's backdrop does. */
.modal--anchored {
	display: block;
	padding: 0;
	background: transparent;
	backdrop-filter: none;
	animation: none;
}
.modal--anchored .modal__panel {
	position: absolute;
	margin: 0;
	max-width: min(480px, 92vw);
	box-shadow: 0 12px 32px rgba(27, 34, 43, 0.22);
	animation: potFade 0.15s ease both;
}

/* ---------------- Modal (confirmation dialog) ---------------- */
.modal__panel--confirm {
	max-width: min(360px, 92vw);
}
.confirm-modal__text {
	font-size: 13.5px;
	color: var(--ink-muted);
	line-height: 1.5;
	margin-bottom: 16px;
}
.confirm-modal__error {
	color: var(--danger-ink);
	margin-bottom: 10px;
}
.confirm-modal__actions {
	display: flex;
	gap: 10px;
}
.confirm-modal__actions .btn-primary,
.confirm-modal__actions .btn-social {
	flex: 1;
	justify-content: center;
}
.btn-primary--danger {
	background: var(--danger-ink);
	box-shadow: 0 2px 0 #8a3a4c;
}

/* ---------------- Appearance photo reorder ---------------- */
.appearance-reorder__list {
	list-style: none;
	margin: 0 0 16px;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 8px;
}
.appearance-reorder__row {
	display: flex;
	align-items: center;
	gap: 10px;
}
.appearance-reorder__thumb {
	width: 48px;
	height: 48px;
	object-fit: cover;
	border-radius: var(--r-card);
	border: 1px solid var(--hairline);
	background: var(--surface-alt);
	flex-shrink: 0;
}
.appearance-reorder__caption {
	flex: 1;
	font-size: 13.5px;
	color: var(--ink-muted);
}
.appearance-reorder__move {
	width: 28px;
	height: 28px;
	border: 1px solid var(--hairline);
	border-radius: var(--r-btn);
	background: var(--surface);
	cursor: pointer;
}
.appearance-reorder__move:disabled {
	opacity: 0.35;
	cursor: default;
}
.appearance-reorder__actions {
	display: flex;
	gap: 10px;
}
.appearance-reorder__actions .btn-primary,
.appearance-reorder__actions .btn-social {
	flex: 1;
	justify-content: center;
}

/* ---------------- Footer ---------------- */
footer {
	border-top: 1px solid var(--hairline);
	background: var(--surface-alt);
	padding: 18px 14px;
}
.footer__in {
	max-width: var(--maxw-wide);
	margin: 0 auto;
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
	align-items: center;
	justify-content: space-between;
}
.footer__attr {
	font-size: 12px;
	color: #8a9098;
	line-height: 1.5;
	max-width: 560px;
}
.footer__links {
	display: flex;
	gap: 14px;
	font-size: 12.5px;
	font-weight: 600;
}

/* ---------------- Bottom tab bar (mobile) ---------------- */
.tabbar {
	position: fixed;
	bottom: 0;
	left: 0;
	right: 0;
	z-index: 50;
	background: rgba(255, 255, 255, 0.94);
	backdrop-filter: blur(12px);
	border-top: 1px solid var(--hairline);
	display: grid;
	grid-template-columns: repeat(7, 1fr);
	padding: 6px 4px 8px;
}
.tabbar a {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 3px;
	padding: 4px 0;
	color: #6a7078;
	font-size: 10px;
	font-weight: 600;
}
.tabbar a.is-active {
	color: var(--accent-deep);
}

/* Profile tab reuses the topbar usermenu (button + popover) instead of a
   plain link so mobile gets the same Profile/Contributions/Moderation/Admin
   menu as desktop rather than jumping straight to the profile page. */
.tabbar .usermenu--tab {
	margin-left: 0;
	position: static;
}
.tabbar .usermenu--tab > [data-usermenu-toggle] {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 3px;
	padding: 4px 0;
	width: 100%;
	background: none;
	border: 0;
	color: #6a7078;
	font-size: 10px;
	font-weight: 600;
	font-family: inherit;
	cursor: pointer;
}
.tabbar .usermenu--tab > [data-usermenu-toggle].is-active {
	color: var(--accent-deep);
}
.usermenu__pop--tab {
	position: fixed;
	top: auto;
	bottom: calc(env(safe-area-inset-bottom, 0px) + 62px);
	right: 0;
	left: auto;
	min-width: 200px;
}
.usermenu__pop--tab .usermenu__item {
	font-size: 15px;
	text-align: right;
	padding: 11px 14px;
}

/* ---------------- Desktop (>=820) ---------------- */
@media (min-width: 820px) {
	.topbar__in {
		padding: 11px var(--gutter);
	}
	main {
		padding: 22px var(--gutter) 40px;
	}
	.nav {
		display: flex;
	}
	.tabbar {
		display: none;
	}
	.jump-top {
		bottom: 24px;
	}
	main {
		padding-bottom: 40px;
	}
	.hero {
		padding: 34px 30px;
	}
	.hero h1 {
		font-size: 40px;
	}
	.home-cols {
		grid-template-columns: 1.05fr 1fr 0.92fr;
	}
	/* Sized for 5 rows of .list-row content (the taller "Latest reports" row,
     two sub-lines) so the column doesn't shrink to fit whatever's published
     right now -- exact px measured from real rendered rows, see home.html.
     Desktop only: on mobile's single-column stack this would just waste
     scroll space under a short list. */
	.panel--rows5 {
		min-height: 410px;
	}
	/* Below wide desktop there's no 4th track for it yet -- runs full width as
     its own row rather than squeezing under just the first column. */
	.showcase-col {
		grid-column: 1 / -1;
	}
	/* Fixed 3-column count (not auto-fit) — with only 3 support cards, auto-fit
     collapses unused tracks and strands the cards left of a dead gap once
     `main` stretches past ~1000px on desktop monitors. */
	.support-grid {
		grid-template-columns: repeat(3, 1fr);
	}
	.banner-carousel {
		height: 88px;
	}
	.banner-slide__subtitle {
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}
	.card-grid {
		grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
	}
	/* Text/stat-heavy, not a card grid — stays reading-width even when `main`
     stretches to --maxw-wide on large monitors, so stat rows and source
     cards don't sprawl into sparse, hard-to-scan single lines. */
	.detail {
		grid-template-columns: 260px 1fr;
		max-width: 1160px;
		margin: 0 auto;
	}
	.page-title {
		font-size: 26px;
	}
	footer {
		padding: 18px var(--gutter);
	}
}

/* ---------------- Wide desktop (>=1440) ---------------- */
@media (min-width: 1440px) {
	.card-grid {
		grid-template-columns: repeat(auto-fill, minmax(258px, 1fr));
		gap: 14px;
	}
	[data-chunks] {
		gap: 14px;
	}
	/* 4 real columns: spots, reports, the CTA pair (as one column), showcase. */
	.home-cols {
		grid-template-columns: 1.1fr 1fr 0.85fr 0.85fr;
		gap: 18px;
	}
	.showcase-col {
		grid-column: auto;
	}
	/* A definite (not content-derived) row height, matching .panel--rows5 --
     without it the showcase thumbnail wrap would inflate the row to fit
     every thumbnail instead of being clipped to the row's real height. */
	.home-cols--farm {
		grid-auto-rows: 410px;
	}
	.hero__row {
		display: flex;
		flex-direction: row;
		align-items: flex-start;
		gap: 28px;
	}
	.hero__in {
		max-width: 620px;
		flex: 1 1 620px;
	}
	.hero__trending {
		display: block;
		flex: 1 1 auto;
		min-width: 0;
	}
}

/* ---------------- Group / element / mode tints ---------------- */
.thumb__img,
.media-card__img {
	background-color: var(--tile-bg, #eeede7);
}
.thumb__id {
	color: var(--tile-ink, #6a7078);
}
.grp-weapon {
	--tile-bg: var(--g-weapon-bg);
	--tile-ink: var(--g-weapon-ink);
}
.grp-armor {
	--tile-bg: var(--g-armor-bg);
	--tile-ink: var(--g-armor-ink);
}
.grp-additional {
	--tile-bg: var(--g-additional-bg);
	--tile-ink: var(--g-additional-ink);
}
.grp-crysta {
	--tile-bg: var(--g-crysta-bg);
	--tile-ink: var(--g-crysta-ink);
}
.grp-material {
	--tile-bg: var(--g-material-bg);
	--tile-ink: var(--g-material-ink);
}
.grp-special {
	--tile-bg: var(--g-special-bg);
	--tile-ink: var(--g-special-ink);
}

.tile-bg {
	background: var(--tile-bg);
	color: #fff;
}
.el-neutral {
	--tile-bg: #000;
}
.el-dark {
	--tile-bg: #7e72a6;
}
.el-earth {
	--tile-bg: #b99a5e;
}
.el-fire {
	--tile-bg: #d9825f;
}
.el-water {
	--tile-bg: #4fa3dc;
}
.el-wind {
	--tile-bg: #5fa97f;
}
.el-light {
	--tile-bg: #d6b85a;
}

/* Map.icon_type badge colors, mirrors catalog/css/map.css .node--* palette */
.mapicon-town {
	--tile-bg: var(--accent);
}
.mapicon-plains {
	--tile-bg: #4f9d5a;
}
.mapicon-mountains {
	--tile-bg: #6b4fa0;
}
.mapicon-ruins {
	--tile-bg: #8a7b52;
}
.mapicon-forest {
	--tile-bg: #2f6b3a;
}
.mapicon-special {
	--tile-bg: var(--ink-muted);
}

.mode-normal {
	color: #6a7078;
	background: var(--hairline-soft);
}
.mode-mini {
	color: var(--material-ink);
	background: var(--material-bg);
}
.mode-boss {
	color: var(--danger-ink);
	background: var(--danger-bg);
}

.tier-suffix {
	color: var(--danger-ink);
	font-weight: 600;
}

.tile-item {
	background: var(--accent-tint);
	color: var(--accent-deep);
}
.tile-drop {
	background: var(--hairline-soft);
	color: var(--ink-muted-2);
}

/* pagination */
.pager {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 10px;
	margin-top: 18px;
}
/* display:flex beats the UA [hidden] default (author rule wins) — same fix as
   .sec-body[hidden] etc. Needed when catalog.js hides the pager on takeover. */
.pager[hidden] {
	display: none;
}
.pager a,
.pager span {
	font-size: 13px;
	font-weight: 600;
}
.pager .btn {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	padding: 8px 14px;
	border: 1px solid var(--hairline);
	border-radius: 9px;
	background: var(--surface);
	color: var(--ink-2);
}
.pager .btn:hover {
	border-color: var(--accent-tint-border);
	color: var(--accent-deep);
}

/* Floating "Jump to Top" (catalog.js). Sits above the mobile tabbar (z 50),
   below modals (z 80). Hidden until the user scrolls ~1.5 viewports down. */
.jump-top {
	position: fixed;
	right: 16px;
	bottom: 76px;
	z-index: 60;
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 10px 14px;
	border: 1px solid var(--accent-tint-border);
	border-radius: var(--r-pill);
	background: var(--surface);
	color: var(--accent-deep);
	font: 600 13px var(--font-body);
	box-shadow: var(--shadow-lift);
	cursor: pointer;
	opacity: 0;
	transform: translateY(8px);
	pointer-events: none;
	transition:
		opacity 0.2s ease,
		transform 0.2s ease;
}
.jump-top.is-visible {
	opacity: 1;
	transform: none;
	pointer-events: auto;
}
.jump-top:hover {
	border-color: var(--accent-deep);
}
@media (prefers-reduced-motion: reduce) {
	.jump-top {
		transition: none;
	}
}
.pager .muted {
	color: var(--ink-faint);
}

.dot--pending {
	background: var(--warn-dot);
}
.dot--approved {
	background: var(--success);
}
.dot--rejected {
	background: var(--danger-ink);
}
.dot--draft {
	background: var(--ink-faint);
}
.dot--ready {
	background: var(--accent);
}
.dot--queued {
	background: var(--warn-dot);
}
.dot--on_review {
	background: var(--accent-deep);
}
.dot--change_requested {
	background: var(--material-ink);
}
.dot--escalated {
	background: var(--g-special-ink);
}
.dot--challenged {
	background: var(--warn-ink);
}

/* ---------------- Auth (login/signup/logout) ---------------- */
.auth-card {
	max-width: 400px;
	margin: 40px auto;
}
.auth-card__title {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 22px;
	letter-spacing: -0.02em;
	margin-bottom: 6px;
}
.auth-card__sub {
	font-size: 13.5px;
	color: var(--ink-muted);
	margin-bottom: 18px;
}
.btn-social {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	width: 100%;
	height: 42px;
	border-radius: 10px;
	border: 1px solid var(--hairline);
	background: var(--surface);
	color: var(--ink);
	font-size: 14px;
	font-weight: 600;
	cursor: pointer;
	text-decoration: none;
}
.btn-social:hover {
	border-color: var(--accent-tint-border);
	color: var(--ink);
}
.btn-social--discord {
	background: #5865f2;
	border-color: #5865f2;
	color: #fff;
}
.btn-social--discord:hover {
	color: #fff;
	opacity: 0.92;
}
.auth-divider {
	display: flex;
	align-items: center;
	gap: 10px;
	margin: 16px 0;
	font-size: 12px;
	color: var(--ink-faint);
}
.auth-divider::before,
.auth-divider::after {
	content: "";
	flex: 1;
	height: 1px;
	background: var(--hairline);
}
.auth-foot {
	margin-top: 16px;
	font-size: 13px;
	color: var(--ink-muted);
	text-align: center;
}

/* ---------------- Alerts / toasts ---------------- */
.alert {
	display: flex;
	align-items: flex-start;
	gap: 9px;
	padding: 11px 13px;
	border-radius: 12px;
	font-size: 13px;
	line-height: 1.45;
	margin-top: 12px;
	border: 1px solid transparent;
}
.alert--danger,
.alert--error {
	background: var(--danger-bg);
	color: var(--danger-ink);
	border-color: rgba(176, 72, 95, 0.22);
}
.alert--success {
	background: #ecf7f0;
	color: var(--success-2);
	border-color: rgba(95, 169, 127, 0.25);
}
.alert--warning {
	background: var(--warn-bg);
	color: var(--warn-ink);
	border-color: rgba(154, 114, 32, 0.22);
}
.alert--info,
.alert--debug {
	background: var(--accent-tint);
	color: var(--accent-deep);
	border-color: var(--accent-tint-border);
}

.toast-stack {
	position: fixed;
	top: 14px;
	right: 14px;
	z-index: 100;
	display: flex;
	flex-direction: column;
	gap: 10px;
	width: min(340px, calc(100vw - 28px));
}
.toast-stack .alert {
	margin-top: 0;
	box-shadow: 0 4px 14px rgba(27, 34, 43, 0.14);
	cursor: pointer;
	animation: potFade 0.28s ease both;
}
.toast-stack .alert.is-leaving {
	animation: potFadeOut 0.18s ease both;
}
@keyframes potFadeOut {
	from {
		opacity: 1;
		transform: translateY(0);
	}
	to {
		opacity: 0;
		transform: translateY(-6px);
	}
}

/* ---------------- Landing page (farming pivot) ---------------- */
/* Extends the locked hero rather than replacing it: same tint gradient, same
   blobs, same solid-offset button shadow. What changes is that the mascot now
   carries the fold, and every number on the page is read from the database. */

.hero--farm {
	padding: 26px 20px 0;
}
.hero__row--farm {
	display: grid;
	grid-template-columns: 1fr;
	gap: 4px;
	align-items: end;
}
.hero--farm h1 {
	font-size: clamp(30px, 7vw, 44px);
}
.hero--farm .hero__in {
	padding-bottom: 22px;
}

.hero__cta {
	display: flex;
	align-items: center;
	gap: 16px;
	margin-top: 20px;
	flex-wrap: wrap;
}
.hero__facts {
	margin-top: 16px;
	font-size: 11.5px;
	color: var(--ink-faint);
	letter-spacing: 0.01em;
}

/* Three ways in. Numbered rows, not a grid of identical icon cards: the modes
   are not equivalent (03 works with zero player data; 01 and 02 need reports). */
.modes {
	display: grid;
	gap: 10px;
	margin-bottom: 22px;
}
.mode {
	display: flex;
	align-items: center;
	gap: 14px;
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: var(--r-card);
	padding: 14px 16px;
	box-shadow: 0 1px 2px rgba(27, 34, 43, 0.03);
	transition:
		transform 0.22s cubic-bezier(0.22, 1, 0.36, 1),
		box-shadow 0.22s,
		border-color 0.22s;
}
.mode:hover {
	transform: translateY(-2px);
	border-color: var(--accent-tint-border);
	box-shadow: 0 4px 14px rgba(79, 163, 220, 0.08);
}
.mode__num {
	flex-shrink: 0;
	width: 34px;
	height: 34px;
	border-radius: 9px;
	display: grid;
	place-items: center;
	font-size: 12px;
	font-weight: 700;
}
.mode--trait .mode__num {
	background: var(--accent-tint);
	color: var(--accent-deep);
}
.mode--points .mode__num {
	background: #fbf4e6;
	color: #9a7220;
}
.mode--craft .mode__num {
	background: #fbf0ea;
	color: #b5734e;
}
.mode__body {
	min-width: 0;
	flex: 1;
	display: flex;
	flex-direction: column;
	gap: 2px;
}
.mode__name {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 15px;
	color: var(--ink);
}
.mode__note {
	font-size: 12.5px;
	line-height: 1.45;
	color: var(--ink-muted);
}
.mode__go {
	flex-shrink: 0;
	color: var(--ink-faint);
	font-size: 16px;
	transition:
		transform 0.22s cubic-bezier(0.22, 1, 0.36, 1),
		color 0.22s;
}
.mode:hover .mode__go {
	color: var(--accent-deep);
	transform: translateX(3px);
}

/* Stretch (not start): the two 5-row list panels set the row's height, and
   the CTA column / showcase column both stretch to match it -- the showcase
   grid then clips its thumbnails to whatever that height leaves it. */
.home-cols--farm {
	align-items: stretch;
}

@media (min-width: 820px) {
	.hero--farm {
		padding: 34px 32px 0;
	}
	/* Text leads, mascot answers. Asymmetric on purpose: a centered mascot with
     the headline underneath would read as a template. */
	.hero--farm .hero__in {
		max-width: 620px;
		padding-bottom: 34px;
	}
	.modes {
		grid-template-columns: repeat(3, 1fr);
		gap: 12px;
	}
	.mode {
		flex-direction: column;
		align-items: flex-start;
		gap: 10px;
		padding: 18px;
	}
	.mode__go {
		align-self: flex-end;
		margin-top: -22px;
	}
}

@media (prefers-reduced-motion: reduce) {
	.mode,
	.mode__go {
		transition: none;
	}
	.mode:hover {
		transform: none;
	}
}

/* Default button. Until now only `.cta-dark button` was styled, so every plain
   <button> in the farming forms rendered as a raw browser control. Element
   selector on purpose: the classed buttons (.link-btn, .banner-dot) outrank it
   on specificity and keep their own looks.
   No box-shadow here on purpose: a shared shadow token applied to every plain
   <button> used to leak onto controls that never meant to have one (the card
   action-bar buttons, the zoom thumb, ...) because opting a specific class out
   of a blanket default is easy to miss for one state and forget entirely for
   another -- exactly what happened here. A component that wants a shadow now
   declares its own (see .btn-primary, .cta-dark button, .btn-contribute). */
button {
	background: var(--accent);
	color: #fff;
	border: none;
	cursor: pointer;
	font-family: inherit;
	font-size: 13.5px;
	font-weight: 600;
	padding: 9px 15px;
	border-radius: 9px;
	transition:
		transform 0.16s cubic-bezier(0.22, 1, 0.36, 1),
		background 0.16s;
}
button:hover {
	border: 0;
}
button:active {
	transform: translateY(1px);
}
button:disabled {
	opacity: 0.55;
	cursor: not-allowed;
}

@media (prefers-reduced-motion: reduce) {
	button {
		transition: none;
	}
	button:active {
		transform: none;
	}
}

/* ---------------- Hero banner (landing fold) ---------------- */
/* One card holds the headline and the mascot. The art bleeds to the card's own
   edges: zero padding on the image side, padding only on the copy side, and the
   card clips the overflow so the cloud splash runs into the corners rather than
   floating in whitespace. */

.hero-banner {
	position: relative;
	display: grid;
	grid-template-columns: 1fr;
	background: linear-gradient(
		160deg,
		var(--accent-tint-3) 0%,
		var(--surface) 70%
	);
	border: 1px solid var(--accent-tint-border-2);
	border-radius: var(--r-hero);
	overflow: hidden;
	margin-bottom: 22px;
}

/* Padding lives here, and only here. */
.hero-banner__copy {
	padding: 24px 20px 26px;
}

.hero-banner h1 {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: clamp(30px, 6.4vw, 44px);
	line-height: 1.02;
	letter-spacing: -0.03em;
	color: var(--ink);
}
.hero-banner p {
	font-size: 15px;
	line-height: 1.5;
	color: var(--ink-muted);
	margin-top: 12px;
	max-width: 60ch;
}

/* Full-bleed: no padding, no radius/shadow of its own -- the card's own
   border-radius + overflow:hidden clips it. Below 760px it's full width at
   its natural ratio (no crop); at 760px+ height is capped so the banner
   doesn't dominate the page, so object-fit:cover crops instead of skewing. */
.hero-banner__art {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
}
.hero-banner__art picture {
	display: block;
	width: 100%;
	height: 100%;
}
.hero-banner__art img {
	display: block;
	width: 100%;
	height: auto;
	cursor: pointer;
}
.hero-banner__art img:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 3px;
}

/* Credit tooltip: hidden until the art is hovered, tapped, or focused.
   Tap-to-show on mobile is handled by app.js focusing the image on click;
   this rule only needs to react to that focus, not implement the toggle. */
.hero-art-credit {
	position: absolute;
	right: 28px;
	bottom: 28px;
	background: rgba(27, 34, 43, 0.92);
	color: #fff;
	font-size: 12px;
	line-height: 1.3;
	padding: 7px 11px;
	border-radius: 8px;
	white-space: nowrap;
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
	transform: translateY(4px);
	transition:
		opacity 0.15s ease,
		transform 0.15s ease,
		visibility 0.15s;
	z-index: 2;
}
.hero-art-credit a {
	color: #fff;
	font-weight: 600;
	text-decoration: underline;
	text-underline-offset: 2px;
	margin-left: 4px;
}
.hero-banner__art:hover .hero-art-credit,
.hero-banner__art:focus-within .hero-art-credit {
	opacity: 1;
	visibility: visible;
	pointer-events: auto;
	transform: translateY(0);
}

.hero__cta {
	display: flex;
	align-items: center;
	gap: 16px;
	margin-top: 20px;
	flex-wrap: wrap;
}
.hero__facts {
	margin-top: 16px;
	font-size: 11.5px;
	color: var(--ink-faint);
	letter-spacing: 0.01em;
}

@media (min-width: 760px) {
	/* Copy left, art right. Row height follows the copy (never clip the CTA/
     facts line); the art is capped and centered in that row so the banner
     doesn't tower over the copy. The cap scales off viewport *width* (vw),
     not vh -- vh broke down on short/ultrawide windows, where 35vh could
     shrink to almost nothing while the copy column stayed normal width, so
     the art crop went tiny and top-heavy independent of the actual layout.
     object-fit:cover crops to match instead of skewing. */
	.hero-banner {
		grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
		align-items: stretch;
	}
	.hero-banner__copy {
		padding: 40px 12px 44px 40px;
		align-self: center;
	}
	.hero-banner__art {
		height: clamp(20vw, 30vw, 40vw);
		align-self: center;
	}
	.hero-banner__art img {
		height: 100%;
		object-fit: contain;
		object-position: right;
	}
}

@media (min-width: 1100px) {
	.hero-banner__copy {
		padding: 52px 20px 56px 56px;
	}
}

/* ---------------- Contribute ---------------- */
/* The one page where the site asks for something instead of giving. It has to
   feel like being welcomed into a workshop, not like filing a ticket: hence the
   mascot, the plain-language stakes, and a visible ladder rather than a silent
   queue. */

.contribute {
	max-width: 720px;
	margin-inline: auto;
}

.welcome {
	display: flex;
	align-items: center;
	gap: 16px;
	background: linear-gradient(
		160deg,
		var(--accent-tint-3) 0%,
		var(--canvas) 82%
	);
	border: 1px solid var(--accent-tint-border-2);
	border-radius: var(--r-hero);
	padding: 18px 20px;
	margin-bottom: 18px;
}
.welcome__mascot {
	flex-shrink: 0;
	width: clamp(80px, 18dvw, 132px);
	height: auto;
}
.welcome__body {
	min-width: 0;
}
.welcome__lede {
	font-size: 14px;
	line-height: 1.55;
	color: var(--ink-muted);
	margin-top: 6px;
}
.welcome__status {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 7px;
	font-size: 12.5px;
	color: var(--ink-muted-2);
	margin-top: 10px;
}

.suggest {
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.step__head {
	display: flex;
	align-items: flex-start;
	gap: 12px;
	margin-bottom: 14px;
}
.step__num {
	flex-shrink: 0;
	width: 30px;
	height: 30px;
	border-radius: 9px;
	display: grid;
	place-items: center;
	font-size: 11.5px;
	font-weight: 700;
	background: var(--accent-tint);
	color: var(--accent-deep);
}
.step__hint {
	font-size: 12.5px;
	line-height: 1.5;
	color: var(--ink-faint);
	margin-top: 3px;
}
.step__grid {
	display: grid;
	gap: 12px;
}
/* Stacked fields need the gap; fields sitting side by side in .step__grid do
   not, and the adjacent-sibling rule was pushing the second column down by
   12px, which is what made the row look crooked after a map was picked. */
.step > .field + .field {
	margin-top: 12px;
}

.field__hint {
	display: block;
	font-size: 11.5px;
	color: var(--ink-faint);
	margin-top: 4px;
}
.field__error {
	display: block;
	font-size: 11.5px;
	font-weight: 600;
	color: var(--danger-ink);
	margin-top: 4px;
}
.field select:focus,
.field input:focus,
.field textarea:focus {
	border-color: var(--accent-tint-border);
	box-shadow: 0 0 0 3px rgba(79, 163, 220, 0.1);
}
.field select:disabled {
	background: var(--surface-sunken);
	color: var(--ink-faint);
}
/* .field input's blanket width:100%/height:40px (above) is meant for text/
   number inputs; a radio or checkbox landing in a .field (op pickers,
   delete_recipe) inherited it too and rendered as a stretched, oversized
   blob. Higher specificity here wins regardless of source order. */
.field input[type="checkbox"],
.field input[type="radio"] {
	width: 16px;
	height: 16px;
	padding: 0;
	border-radius: 4px;
	accent-color: var(--accent);
	flex-shrink: 0;
	cursor: pointer;
}
.field input[type="radio"] {
	border-radius: 50%;
}

.sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

.field--checkbox {
	display: flex;
	align-items: center;
	gap: 8px;
	cursor: pointer;
}
.field--checkbox > span {
	font-size: 13.5px;
	color: var(--ink);
}

/* Segmented control: RadioSelect fields whose choices are mutually exclusive
   actions (add vs. remove), rendered as a two-way toggle instead of raw radio
   dots + Django's default <ul> list markup. */
.seg {
	display: inline-flex;
	border: 1px solid var(--hairline);
	border-radius: 9px;
	overflow: hidden;
	width: 100%;
}
.seg__opt {
	flex: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	padding: 10px 12px;
	font-size: 13px;
	font-weight: 600;
	color: var(--ink-muted);
	background: var(--surface);
	cursor: pointer;
	text-align: center;
	transition:
		background 0.15s,
		color 0.15s;
}
.seg__opt + .seg__opt {
	border-left: 1px solid var(--hairline);
}
.seg__opt:hover {
	background: var(--row-hover);
}
.seg__opt input {
	position: absolute;
	width: 1px;
	height: 1px;
	opacity: 0;
	pointer-events: none;
}
.seg__opt:has(input:checked) {
	background: var(--accent-tint);
	color: var(--accent-deep);
}
.seg__opt:has(input:focus-visible) {
	outline: 2px solid var(--accent);
	outline-offset: -2px;
}

/* Recipe-materials / item-stats formset rows: a picker + a couple of narrow
   numeric fields + a remove button, previously dumped with no wrapper and no
   CSS at all (bare native controls, no gap, no labels). */
.field-row-group__head {
	display: grid;
	grid-template-columns: 1fr 84px 64px 36px;
	gap: 8px;
	padding: 0 2px 4px;
	font-size: 10px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--ink-faint);
}
.field-row {
	display: grid;
	grid-template-columns: 1fr 84px 64px 36px;
	gap: 8px;
	align-items: start;
	margin-bottom: 8px;
}
.field-row select,
.field-row input:not([type="checkbox"]) {
	width: 100%;
	height: 40px;
	padding: 0 10px;
	border: 1px solid var(--hairline);
	border-radius: 9px;
	font-size: 14px;
	color: var(--ink);
	background: var(--surface);
	outline: none;
}
.field-row select:focus,
.field-row input:focus {
	border-color: var(--accent-tint-border);
	box-shadow: 0 0 0 3px rgba(79, 163, 220, 0.1);
}
.field-row__remove {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 36px;
	height: 40px;
	border: 1px solid var(--hairline);
	border-radius: 9px;
	color: var(--ink-faint);
	background: var(--surface);
	cursor: pointer;
	transition:
		background 0.15s,
		color 0.15s,
		border-color 0.15s;
}
.field-row__remove:hover {
	background: var(--danger-bg);
	color: var(--danger-ink);
	border-color: var(--danger-ink);
}
.field-row__remove input {
	position: absolute;
	width: 1px;
	height: 1px;
	opacity: 0;
	pointer-events: none;
}
.field-row__remove:has(input:checked) {
	background: var(--danger-bg);
	color: var(--danger-ink);
	border-color: var(--danger-ink);
}

/* Item-stats row: effect + amount + remove on one line, "applies to" chips
   (reusing .checks/.check, just denser) wrapping full-width beneath.
   Named .stat-edit-row, not .stat-row -- that name is already the item-detail
   stat display row (line ~1392) and calculator.css reuses it too; colliding
   would have bled this form's spacing into both. */
.stat-edit-row-group__head {
	display: grid;
	grid-template-columns: 1fr 100px 36px;
	gap: 8px;
	padding: 0 2px 4px;
	font-size: 10px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--ink-faint);
}
.stat-edit-row {
	margin-bottom: 12px;
	padding-bottom: 12px;
	border-bottom: 1px solid var(--hairline-soft);
}
.stat-edit-row:last-child {
	margin-bottom: 0;
	padding-bottom: 0;
	border-bottom: none;
}
.stat-edit-row__main {
	display: grid;
	grid-template-columns: 1fr 100px 36px;
	gap: 8px;
	align-items: start;
	margin-bottom: 8px;
}
.stat-edit-row__main select,
.stat-edit-row__main input {
	width: 100%;
	height: 40px;
	padding: 0 10px;
	border: 1px solid var(--hairline);
	border-radius: 9px;
	font-size: 14px;
	color: var(--ink);
	background: var(--surface);
}
.checks--wrap {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	padding: 2px 2px 0;
}
.check--sm {
	padding: 6px 10px;
	border-radius: 20px;
	background: var(--surface);
	gap: 6px;
}
.check--sm .check__name {
	font-size: 12.5px;
	font-weight: 500;
}

.checks {
	display: grid;
	gap: 10px;
}
.check {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	cursor: pointer;
	border: 1px solid var(--hairline);
	border-radius: var(--r-card);
	padding: 12px 14px;
	background: var(--surface-sunken);
	transition:
		border-color 0.16s,
		background 0.16s;
}
.check:hover {
	border-color: var(--accent-tint-border);
	background: var(--row-hover);
}
.check input {
	margin-top: 2px;
	width: 16px;
	height: 16px;
	accent-color: var(--accent);
	flex-shrink: 0;
}
.check__name {
	display: block;
	font-size: 13.5px;
	font-weight: 600;
	color: var(--ink);
}
.check__note {
	display: block;
	font-size: 12px;
	line-height: 1.45;
	color: var(--ink-faint);
	margin-top: 2px;
}

/* Six material rows, all visible. A mob drops several kinds, and "fill the two
   you know" beats add-row / choose / type every time. */
.yields {
	border: 1px solid var(--hairline);
	border-radius: var(--r-card);
	overflow: hidden;
}
.yields__head {
	display: grid;
	grid-template-columns: 1fr 1fr 1fr;
	gap: 8px;
	padding: 8px 12px;
	background: var(--surface-sunken);
	font-size: 10px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--ink-faint);
}
.yield-row {
	display: grid;
	grid-template-columns: 1fr 1fr 1fr;
	gap: 8px;
	align-items: center;
	padding: 7px 12px;
	border-top: 1px solid var(--hairline-soft);
}
.yield-row__name {
	font-size: 13px;
	font-weight: 600;
	color: var(--ink-2);
}
.yield-row input {
	width: 100%;
	height: 34px;
	padding: 0 9px;
	font-size: 13.5px;
	border: 1px solid var(--hairline);
	border-radius: 8px;
	background: var(--surface);
	color: var(--ink);
}
.yield-row input:focus {
	outline: none;
	border-color: var(--accent-tint-border);
	box-shadow: 0 0 0 3px rgba(79, 163, 220, 0.1);
}
.yield-row__mins {
	display: flex;
	align-items: center;
	gap: 6px;
}
.yield-row__mins em {
	font-style: normal;
	font-size: 11.5px;
	color: var(--ink-faint);
}
.yield-row__error {
	padding: 0 12px 6px;
}
/* .yield-row sets display:grid, which otherwise beats the UA [hidden] default
   (same specificity, author rule wins) -- see .sec-body[hidden]/.modal[hidden]
   above for the same fix. */
.yield-row--extra[hidden] {
	display: none;
}

/* Trait rows: same table chrome as the material/specific-item rows above,
   but one column — a trait pick is presence-only, no amount/duration. */
.yields__head--single,
.yield-row--single {
	grid-template-columns: 1fr;
}

.suggest__submit {
	display: flex;
	flex-direction: column;
	gap: 8px;
	align-items: flex-start;
	margin-top: 4px;
}

/* Trust as a visible path, not a mystery. A contributor should always be able to
   see what the next rung costs. */
.ladder {
	display: grid;
	gap: 2px;
	margin-bottom: 20px;
}
.rung {
	display: flex;
	gap: 12px;
	align-items: flex-start;
	padding: 12px 14px;
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: var(--r-card);
}
.rung.is-current {
	border-color: var(--accent-tint-border);
	background: var(--accent-tint);
}
.rung__dot {
	flex-shrink: 0;
	width: 9px;
	height: 9px;
	border-radius: 50%;
	margin-top: 6px;
	background: var(--hairline);
	border: 1px solid var(--ink-faint);
}
.rung.is-current .rung__dot {
	background: var(--accent);
	border-color: var(--accent-deep);
}
.rung__title {
	font-size: 13.5px;
	font-weight: 700;
	color: var(--ink);
	display: flex;
	align-items: center;
	gap: 8px;
}
.rung__you {
	font-size: 9.5px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--accent-deep);
	background: #fff;
	border: 1px solid var(--accent-tint-border);
	padding: 2px 6px;
	border-radius: 5px;
}
.rung__blurb {
	font-size: 12.5px;
	line-height: 1.45;
	color: var(--ink-muted);
	margin-top: 2px;
}
.rung__cost {
	font-size: 11px;
	color: var(--ink-faint);
	margin-top: 3px;
}

/* Report status. The model says "published"; the existing chip css only knew
   "approved". */
.status {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	flex-shrink: 0;
	font-size: 11px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	padding: 3px 8px;
	border-radius: 6px;
}
.status--published {
	color: var(--success-2);
	background: #ecf7f0;
}
.dot--pending {
	background: var(--warn-dot);
}
.dot--published {
	background: var(--success);
}
.dot--rejected {
	background: var(--danger-ink);
}

.empty__mascot {
	display: block;
	width: 40dvw;
	height: auto;
	margin: 0 auto 10px;
	opacity: 0.9;
}

@media (min-width: 700px) {
	.welcome {
		padding: 22px 26px;
		gap: 22px;
	}
	.step__grid {
		grid-template-columns: 1fr 1fr;
	}
}

@media (max-width: 520px) {
	/* Three columns of numbers do not fit a phone. Stack, and label each input,
     because a bare number box with no label is a guess. */
	.yields__head {
		display: none;
	}
	.yield-row {
		grid-template-columns: 1fr;
		gap: 6px;
		padding: 10px 12px;
	}
	.yield-row__name {
		font-size: 12.5px;
	}
	.welcome {
		flex-direction: column;
		text-align: center;
	}
}

/* ---------------- Searchable select ---------------- */
/* A native <select> of 555 maps means scrolling by eye. This is a text input
   over the real select, which stays in the DOM and still submits, so the form
   works with JS off. */

.combo {
	position: relative;
}
.combo__native {
	/* Not display:none — a hidden-but-present select keeps working as the form
     control and stays reachable if the script fails to run. */
	position: absolute;
	opacity: 0;
	pointer-events: none;
	width: 100%;
	height: 40px;
	left: 0;
	top: 0;
}
.combo:not(.combo--open) .combo__native {
	z-index: -1;
}

.combo__input {
	width: 100%;
	height: 40px;
	padding: 0 32px 0 10px;
	border: 1px solid var(--hairline);
	border-radius: 9px;
	font-size: 14px;
	color: var(--ink);
	background: var(--surface);
	outline: none;
}
.combo__input:focus {
	border-color: var(--accent-tint-border);
	box-shadow: 0 0 0 3px rgba(79, 163, 220, 0.1);
}
.combo__input:disabled {
	background: var(--surface-sunken);
	color: var(--ink-faint);
}
.combo::after {
	content: "";
	position: absolute;
	right: 12px;
	top: 17px;
	width: 7px;
	height: 7px;
	pointer-events: none;
	border-right: 1.5px solid var(--ink-faint);
	border-bottom: 1.5px solid var(--ink-faint);
	transform: rotate(45deg) translateY(-2px);
}

.combo__list {
	/* position:fixed (not absolute): the edit modal nests this inside
	   .panel/.modal__panel (overflow:hidden) and .modal__body
	   (overflow-y:auto), any of which would clip an absolutely-positioned
	   list before it ever became visible. Fixed positioning is placed by JS
	   (combobox.js) from the input's viewport rect, so it escapes all of
	   that clipping instead of needing a .step--where-style override per
	   container. */
	display: none;
	z-index: 1001;
	max-height: 264px;
	overflow-y: auto;
	list-style: none;
	margin-top: 10px;
	padding: 4px;
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: var(--r-card);
	box-shadow: 0 8px 24px rgba(27, 34, 43, 0.1);
}
.combo--open .combo__list {
	display: block;
}
.combo__option {
	padding: 8px 10px;
	border-radius: 7px;
	cursor: pointer;
	font-size: 13.5px;
	color: var(--ink);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.combo__option.is-active {
	background: var(--accent-tint);
	color: var(--accent-deep);
}
.combo__option--disabled {
	opacity: 0.4;
	cursor: not-allowed;
}
.combo__option--disabled.is-active {
	background: none;
	color: var(--ink);
}
.combo__none {
	padding: 8px 10px;
	font-size: 12.5px;
	color: var(--ink-faint);
}

/* Color swatches for dye pickers (DyeSelect widget, forms.py) — a no-op for
   every other combo, since only dye <option>s carry data-hex. */
.combo--swatch .combo__input {
	padding-left: 34px;
}
.combo__swatch {
	position: absolute;
	left: 10px;
	top: 12px;
	width: 16px;
	height: 16px;
	border-radius: 4px;
	border: 1px solid transparent;
	pointer-events: none;
}
.combo__option-swatch {
	display: inline-block;
	width: 12px;
	height: 12px;
	border-radius: 3px;
	border: 1px solid var(--hairline);
	margin-right: 6px;
	vertical-align: middle;
}

/* Multi-select combos (e.g. applies_to equipment types) show chosen options
   as removable chips above the search input instead of filling the input
   itself -- there is no single "current value" to mirror into text. */
.combo__chips {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
}
.combo__chips:not(:empty) {
	margin-bottom: 6px;
}
.combo__chip {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	padding: 3px 4px 3px 10px;
	border-radius: 999px;
	background: var(--accent-tint);
	color: var(--accent-deep);
	font-size: 12.5px;
	white-space: nowrap;
}
.combo__chip-remove {
	border: none;
	background: none;
	color: inherit;
	font-size: 15px;
	line-height: 1;
	cursor: pointer;
	padding: 2px 4px;
	border-radius: 999px;
}
.combo__chip-remove:hover {
	background: rgba(0, 0, 0, 0.08);
}

/* Suggest-an-edit forms (edit_form_body.html) stack several [data-edit-form]
   panels and, within each, several .field rows back to back with zero gap
   of their own -- reads as one packed wall of controls at any width. */
[data-edit-form] + [data-edit-form] {
	margin-top: 24px;
}
[data-edit-form] .field + .field {
	margin-top: 18px;
}
[data-edit-form] .field + .btn-primary {
	margin-top: 18px;
}
.combo__chips:not(:empty) {
	margin-bottom: 10px;
}
.combo__chips {
	gap: 8px;
}

/* Step 01's two fields sit in a grid. The monster hint only appears once a map
   is chosen, and without reserved space the whole row jumped when it did. */
.step__grid {
	align-items: start;
}
.step__grid .field {
	min-width: 0;
}
.field__hint {
	min-height: 15px;
}

/* .panel sets overflow:hidden for its rounded corners, which clips the combobox
   dropdown to the card. This is the one panel that has to let a child escape,
   and it needs to stack above the step below it while the list is open. */
.step--where {
	overflow: visible;
	position: relative;
	z-index: 5;
}

/* Same fix, for the "Farming specific items" combobox rows: .panel and .yields
   both set overflow:hidden for their own rounded corners, and both clip the
   combobox dropdown the same way .step--where clips the map/monster one. */
.step--yields {
	overflow: visible;
	position: relative;
	z-index: 5;
}
.step--yields .yields {
	overflow: visible;
}
.yields__head {
	border-radius: var(--r-card) var(--r-card) 0 0;
}

/* ---- Moderation queue (community:moderate) ---- */
/* Many stacked sections (review queue, farming reports, comment reports, held
   images), not a sidebar+content pair -- .detail's desktop two-column grid
   auto-places flat siblings into a left/right zigzag with no relation to
   section boundaries, so this page gets its own single-column wrapper
   instead of reusing .detail. */
.mod-page {
	display: grid;
	grid-template-columns: 1fr;
	gap: 16px;
}
@media (min-width: 820px) {
	.mod-page {
		max-width: 1160px;
		margin: 0 auto;
	}
}

.mod-actions {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.5rem;
	border-top: 1px solid var(--hairline-soft);
	padding-top: 0.5rem;
	width: 100%;
}
.mod-actions form {
	display: flex;
	align-items: center;
	gap: 6px;
	margin: 0;
	width: 100%;
}

.mod-appearance-btn {
	width: 100%;
	transition: filter 0.2s ease;
}

.mod-appearance-btn:hover {
	filter: brightness(0.85);
}

.mod-actions input[type="text"] {
	min-width: 180px;
	padding: 7px 10px;
	border: 1px solid var(--hairline);
	border-radius: 8px;
	background: var(--surface-sunken);
	color: var(--ink-2);
	font: inherit;
	font-size: 12.5px;
}

/* ---- Three-dot entity menu (cards + detail headers) ---- */
/* Inline by default (detail headers sit it next to the title); absolute only
   inside a card, where it hangs in the top-right corner. */
.ent-menu {
	position: relative;
}
.card {
	position: relative;
}
.card > .ent-menu {
	position: absolute;
	top: 6px;
	right: 6px;
	z-index: 3;
}

.ent-menu__btn {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 26px;
	height: 26px;
	padding: 0;
	border: 0;
	border-radius: 8px;
	background: transparent;
	color: var(--ink-faint);
	cursor: pointer;
}
.ent-menu__btn:hover {
	background: var(--surface-alt);
	color: var(--ink-2);
}
.ent-menu__btn[aria-expanded="true"] {
	background: var(--accent-tint);
	color: var(--accent-deep);
}

.ent-menu__pop {
	position: absolute;
	top: 30px;
	right: 0;
	z-index: 20;
	min-width: 172px;
	padding: 4px;
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: 10px;
	box-shadow: 0 6px 20px rgba(27, 34, 43, 0.12);
}
.ent-menu__row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	padding: 8px 10px;
	border-radius: 8px;
	font-size: 12.5px;
	font-weight: 600;
	color: var(--ink-2);
	text-decoration: none;
	white-space: nowrap;
}
.ent-menu__row:hover {
	background: var(--row-hover);
	color: var(--accent-deep);
}
.ent-menu__count {
	color: var(--ink-faint);
	font-weight: 500;
}

/* ---- Inline detail thread (edit + comments rendered on the detail page) ---- */
.detail-thread {
	margin-top: 22px;
	padding-top: 18px;
	border-top: 1px solid var(--hairline);
}
.inline-edit {
	margin-bottom: 18px;
}
.inline-edit__body {
	margin-top: 12px;
}
.btn-ghost {
	display: inline-block;
	text-decoration: none;
	background: var(--surface);
	color: var(--ink-2);
	border: 1px solid var(--hairline);
	cursor: pointer;
	font-size: 13px;
	font-weight: 600;
	padding: 8px 14px;
	border-radius: 10px;
}
.btn-ghost:hover {
	border-color: var(--accent);
	color: var(--accent-deep);
	background: var(--surface-alt);
}

/* ---- Comment thread modal ---- */
.modal__panel--wide {
	max-width: min(680px, 94vw);
}

.cmt-sort {
	display: flex;
	gap: 6px;
	margin-bottom: 12px;
}
.cmt-sort__btn {
	padding: 5px 12px;
	border: 1px solid var(--hairline);
	border-radius: 999px;
	background: var(--surface);
	color: var(--ink-muted);
	font-size: 12px;
	font-weight: 600;
	cursor: pointer;
}
.cmt-sort__btn--on {
	background: var(--accent-tint);
	border-color: var(--accent-tint-border);
	color: var(--accent-deep);
}

.cmt {
	padding: 10px 12px;
	margin-bottom: 8px;
	background: var(--surface);
	border: 1px solid var(--hairline-soft);
	border-radius: 12px;
}
.cmt--reply {
	border-left: 3px solid var(--accent-tint-border);
	padding-left: 9px;
}
.cmt__head {
	display: flex;
	align-items: center;
	gap: 6px;
	flex-wrap: wrap;
	font-size: 11.5px;
	font-weight: 700;
	color: var(--ink-muted-2);
}
.cmt__head a {
	color: inherit;
}
.cmt__author {
	display: inline-flex;
	align-items: center;
	gap: 6px;
}
.cmt__avatar {
	width: 22px;
	height: 22px;
	border-radius: 50%;
	object-fit: cover;
	flex-shrink: 0;
	display: grid;
	place-items: center;
	background: var(--accent-tint);
	color: var(--accent-deep);
	font-size: 11px;
	font-weight: 700;
}
.cmt__head a:hover {
	color: var(--accent-deep);
	text-decoration: underline;
}
.cmt__body {
	margin-top: 3px;
	font-size: 13.5px;
	color: var(--ink-2);
	overflow-wrap: anywhere;
}
.cmt__empty {
	padding: 14px;
	color: var(--ink-faint);
	font-size: 13px;
	text-align: center;
}

/* 16:9, fluid — a fixed height letterboxes on a phone. */
.cmt__embed {
	width: 100%;
	aspect-ratio: 16 / 9;
	margin-top: 8px;
	border: 0;
	border-radius: 10px;
	background: var(--surface-alt);
}

.cmt__bar {
	display: flex;
	align-items: center;
	gap: 6px;
	margin-top: 8px;
}
.cmt__vote {
	width: 24px;
	height: 24px;
	padding: 0;
	border: 1px solid var(--hairline);
	border-radius: 7px;
	background: var(--surface);
	color: var(--ink-faint);
	font-size: 11px;
	line-height: 1;
	cursor: pointer;
}
.cmt__vote:hover:not(:disabled) {
	border-color: var(--accent);
	color: var(--accent-deep);
}
.cmt__vote--on {
	background: var(--accent-tint);
	border-color: var(--accent);
	color: var(--accent-deep);
}
.cmt__vote:disabled {
	opacity: 0.45;
	cursor: default;
}
.cmt__score {
	min-width: 20px;
	text-align: center;
	font-size: 12px;
	font-weight: 700;
	color: var(--ink-2);
	font-variant-numeric: tabular-nums;
}
.cmt__action {
	padding: 3px 8px;
	border: 0;
	border-radius: 7px;
	background: transparent;
	color: var(--ink-muted);
	font-size: 11.5px;
	font-weight: 600;
	cursor: pointer;
}
.cmt__action:hover {
	background: var(--surface-alt);
	color: var(--accent-deep);
}

.cmt__reply-box,
.cmt__composer {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 6px;
	margin-top: 8px;
}
.cmt__reply-box input[type="text"],
.cmt__composer input[type="text"],
.cmt__reply-box textarea,
.cmt__composer textarea,
.cmt__reply-box select {
	flex: 1 1 200px;
	min-width: 0;
	padding: 8px 10px;
	border: 1px solid var(--hairline);
	border-radius: 9px;
	background: var(--surface);
	color: var(--ink);
	font: inherit;
	font-size: 13px;
}
.cmt__reply-box textarea,
.cmt__composer textarea {
	resize: vertical;
	min-height: 40px;
}
.cmt__composer {
	padding-top: 12px;
	border-top: 1px solid var(--hairline-soft);
}
.cmt__signin {
	padding-top: 12px;
	border-top: 1px solid var(--hairline-soft);
	font-size: 13px;
	color: var(--ink-muted);
}
.cmt__signin .btn-primary {
	margin-right: 6px;
}

/* ---- Comment image upload + gallery ---- */
.cmt__composer-row {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 6px;
}
.cmt__upload-strip {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin-top: 6px;
}
.cmt__chip {
	padding: 3px 9px;
	border-radius: 999px;
	font-size: 11px;
	font-weight: 600;
	background: var(--surface-alt);
	color: var(--ink-muted);
}
.cmt__chip--ok {
	background: var(--accent-tint);
	color: var(--accent-deep);
}
.cmt__chip--held {
	background: var(--danger-bg);
	color: var(--danger-ink);
}
.cmt__chip--bad {
	background: var(--danger-bg);
	color: var(--danger-ink);
}
.cmt__chip--existing {
	display: inline-flex;
	align-items: center;
	gap: 4px;
}
.cmt__chip-remove {
	border: 0;
	background: transparent;
	color: inherit;
	cursor: pointer;
	font-size: 12px;
	line-height: 1;
	padding: 0 2px;
}
.cmt__chip-remove:hover {
	color: var(--danger-ink);
}
.cmt__attach-wrap {
	display: inline-flex;
	flex-direction: column;
	align-items: center;
	gap: 3px;
}
.cmt__attach {
	position: relative;
	width: 56px;
	height: 56px;
	border-radius: 10px;
	overflow: hidden;
	background: var(--surface-alt);
}
.cmt__attach img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}
.cmt__attach--held {
	outline: 2px dashed var(--danger-ink);
}
.cmt__attach--bad {
	outline: 2px solid var(--danger-ink);
	opacity: 0.6;
}
.cmt__attach-progress {
	position: absolute;
	left: 0;
	bottom: 0;
	height: 3px;
	background: var(--accent);
	width: 0%;
	transition: width 0.15s linear;
}
.cmt__attach-remove {
	position: absolute;
	top: 2px;
	right: 2px;
	width: 18px;
	height: 18px;
	border-radius: 999px;
	border: 0;
	background: rgba(0, 0, 0, 0.55);
	color: #fff;
	font-size: 12px;
	line-height: 1;
	cursor: pointer;
	padding: 0;
}
.cmt__attach-remove:hover {
	background: rgba(0, 0, 0, 0.75);
}
.cmt__attach-caption {
	font-size: 10px;
	color: var(--danger-ink);
	max-width: 56px;
	text-align: center;
}

.cmt__images {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin-top: 8px;
}
.cmt__image,
.cmt__image-wrap {
	max-width: 140px;
	border-radius: 10px;
}
.cmt__image {
	height: auto;
	max-height: 140px;
	object-fit: cover;
	border: 1px solid var(--hairline-soft);
	cursor: zoom-in;
}
.cmt__image-wrap {
	position: relative;
	display: inline-block;
}
.cmt__image--held {
	opacity: 0.9;
	outline: 2px dashed var(--danger-ink);
}
.cmt__held-tag {
	position: absolute;
	left: 4px;
	bottom: 4px;
	right: 4px;
	padding: 2px 5px;
	border-radius: 6px;
	background: rgba(27, 34, 43, 0.78);
	color: #fff;
	font-size: 9.5px;
	font-weight: 600;
	text-align: center;
}

/* ---- Edit history panel ---- */
.cmt__edited {
	border: 0;
	background: transparent;
	padding: 0;
	margin-left: 4px;
	font: inherit;
	font-size: 11.5px;
	font-weight: 700;
	color: var(--ink-faint);
	text-decoration: underline;
	cursor: pointer;
}
.cmt__edited:hover {
	color: var(--accent-deep);
}
.cmt-history__item {
	padding: 10px 0;
	border-bottom: 1px solid var(--hairline-soft);
}
.cmt-history__item:last-child {
	border-bottom: 0;
}
.cmt-history__time {
	font-size: 11px;
	font-weight: 700;
	color: var(--ink-muted-2);
}
.cmt-history__body {
	margin-top: 3px;
	font-size: 13.5px;
	color: var(--ink-2);
	overflow-wrap: anywhere;
}

/* ---- Entity history modal (item/monster/map "History" button) ---- */
.ent-history__item {
	padding: 10px 0;
	border-bottom: 1px solid var(--hairline-soft);
}
.ent-history__item:last-child {
	border-bottom: 0;
}
.ent-history__time {
	font-size: 11px;
	font-weight: 700;
	color: var(--ink-muted-2);
}
.ent-history__body {
	margin-top: 3px;
	font-size: 13.5px;
	color: var(--ink-2);
	overflow-wrap: anywhere;
}
.ent-history__toggle {
	margin-top: 6px;
	border: 0;
	background: transparent;
	padding: 0;
	font: inherit;
	font-size: 11.5px;
	font-weight: 700;
	color: var(--ink-faint);
	text-decoration: underline;
	cursor: pointer;
}
.ent-history__toggle:hover {
	color: var(--accent-deep);
}
.ent-history__diff {
	margin: 6px 0 0;
	font-size: 12.5px;
}
.ent-history__diff dt {
	font-weight: 700;
	color: var(--ink-muted-2);
}
.ent-history__diff dd {
	margin: 0 0 4px;
	color: var(--ink-2);
	overflow-wrap: anywhere;
}
.ent-history__note {
	margin-top: 6px;
	font-size: 12px;
	color: var(--ink-faint);
	font-style: italic;
}

/* Crop overlay */
.cropper {
	position: fixed;
	inset: 0;
	z-index: 1000;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 12px;
	background: rgba(27, 34, 43, 0.72);
	padding: 20px;
}
.cropper__stage {
	position: relative;
	touch-action: none;
	line-height: 0;
}
.cropper__stage canvas {
	display: block;
	border-radius: 8px;
	max-width: 100%;
}
.cropper__sel {
	position: absolute;
	box-sizing: border-box;
	border: 2px solid var(--accent);
	background: rgba(79, 163, 220, 0.18);
	pointer-events: none;
	overflow: hidden;
}
/* Character preview drawn inside the crop box (background picker), so the user
   sees the final composite while positioning the 1:1 crop. */
.cropper__overlay {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	pointer-events: none;
}
.cropper__controls {
	display: flex;
	gap: 8px;
}

/* ---------------- About / FAQ ---------------- */
.about-stats {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
	gap: 12px;
	margin: 16px 0 6px;
}
.about-stat {
	text-align: center;
	padding: 16px 10px;
	background: var(--surface-alt);
	border-radius: var(--r-card);
	border: 1px solid var(--hairline);
}
.about-stat__num {
	display: block;
	font-family: var(--font-display);
	font-weight: 800;
	font-size: 26px;
	color: var(--accent-deep);
}
.about-stat__label {
	font-size: 12px;
	color: var(--ink-muted);
}
.about-stats__note {
	font-size: 12px;
	color: var(--ink-faint);
	margin: 4px 0 0;
}

.support-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 14px;
	margin: 14px 0 20px;
}
.support-card {
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: var(--r-card);
	padding: 16px;
	display: flex;
	flex-direction: column;
	gap: 8px;
}
.support-card h3 {
	font-family: var(--font-display);
	font-size: 15px;
	margin: 0;
}
.support-card p {
	font-size: 13.5px;
	color: var(--ink-muted);
	margin: 0;
}
.support-card--kofi {
	background: var(--accent-tint-3);
	border-color: var(--accent-tint-border-2);
}
.support-card .btn-primary,
.support-card .chip {
	align-self: flex-start;
}

.about-thanks {
	margin-top: 24px;
	padding: 16px;
	text-align: center;
	font-size: 14px;
	color: var(--ink-2);
	border-top: 1px solid var(--hairline-soft);
}

.faq__intro {
	color: var(--ink-muted);
	margin-bottom: 8px;
}
.faq h2.section-title:first-of-type {
	margin-top: 8px;
}

/* Equipment appearance / monster photo galleries
   (spec: 2026-07-17-equipment-monster-appearance-design.md)

   Tile ratio matches the game's own render canvas, not an arbitrary square:
   equipment renders at 260x310 (portrait, ~5:6); monster art at 260x210
   (landscape, ~5:4). .appearance-gallery--item is the default (base rule);
   --monster overrides it. The crop step in appearance-gallery.js enforces
   the same ratio client-side, so every approved photo actually fills the
   tile instead of stretching or letterboxing.

   Each slot collapses to a single "folder" thumbnail (cover photo + a
   stacked-paper edge) instead of one tile per photo -- a slot with a dozen
   dye reports used to wrap into a tall grid; now it's one tap target that
   opens the slideshow modal (openSlideshow, appearance-gallery.js). */
.appearance-gallery {
	display: flex;
	flex-direction: column;
	gap: 16px;
	margin: 12px 0 20px;
}
.appearance-gallery--armor {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 16px;
}
.appearance-slot__label {
	font-size: 12px;
	font-weight: 700;
	color: var(--ink-muted);
	margin-bottom: 6px;
}
.appearance-slot__tiles {
	display: flex;
	align-items: flex-start;
	gap: 14px;
}

.appearance-folder {
	position: relative;
	width: 92px;
	aspect-ratio: 5 / 6;
	flex-shrink: 0;
	border: 0;
	margin: 0;
	padding: 0;
	background: none;
	cursor: pointer;
	font: inherit;
	border-radius: var(--r-card);
	box-shadow:
		4px -4px 0 -1px var(--surface),
		4px -4px 0 0 var(--hairline),
		8px -8px 0 -1px var(--surface),
		8px -8px 0 0 var(--hairline-soft);
}
.appearance-folder--single {
	box-shadow: none;
}
.appearance-folder__cover {
	position: absolute;
	inset: 0;
	display: block;
	border-radius: inherit;
	overflow: hidden;
	background: var(--surface-alt);
	border: 1px solid var(--hairline);
}
.appearance-folder__cover img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
	transition: transform 0.2s ease;
}
.appearance-folder:hover .appearance-folder__cover img {
	transform: scale(1.05);
}
.appearance-folder__count {
	position: absolute;
	right: 5px;
	bottom: 5px;
	padding: 2px 6px;
	border-radius: 999px;
	background: rgba(27, 34, 43, 0.78);
	color: var(--surface);
	font-family: var(--font-mono);
	font-size: 10px;
	font-weight: 700;
	line-height: 1.3;
}
.appearance-folder__pending-dot {
	position: absolute;
	top: 6px;
	left: 6px;
	width: 8px;
	height: 8px;
	border-radius: 999px;
	background: var(--warn-dot);
	border: 1.5px solid var(--surface);
}
.appearance-add {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 92px;
	aspect-ratio: 5 / 6;
	border: 1px dashed var(--hairline);
	border-radius: var(--r-card);
	color: var(--ink-faint);
	font-size: 12px;
	text-decoration: none;
	text-align: center;
	flex-shrink: 0;
}
.appearance-add:hover {
	color: var(--accent-deep);
	border-color: var(--accent-tint-border);
}

.appearance-gallery--monster .appearance-folder,
.appearance-gallery--monster .appearance-add {
	aspect-ratio: 5 / 4;
}

/* Slideshow modal (openSlideshow, appearance-gallery.js) */
/* iOS-style: no forced aspect-ratio and no crop -- the image shows at its
   real ratio, bounded by the viewport, and the panel shrink-wraps it (see
   the shared .appearance-slideshow rule up in the lightbox section). Nav
   arrows overlay the image as glass pills instead of stealing width. */
.appearance-slideshow__body {
	display: block;
	padding: 0;
	position: relative;
	background: transparent;
}
/* Wraps .appearance-slideshow__frame + the nav buttons so top:50% on the
   buttons centers on the image itself -- not on .appearance-slideshow__body,
   which also stacks the dots/credit/admin rows underneath and so has a
   different height per photo (longer credit name, extra dot row on a
   multi-photo item, ...). Positioning against body used to make the arrows
   drift between photos instead of staying centered on the frame. */
.appearance-slideshow__stage {
	position: relative;
}
.appearance-slideshow__frame {
	/* fixed, ratio-locked box instead of sizing to each upload's intrinsic
	   pixel dimensions -- approved photos are already cropped to TILE_RATIO
	   (appearance-gallery.js) so they share an aspect ratio, but not a pixel
	   size, and width:auto/height:auto let the modal balloon or shrink per
	   photo. Ratio matches the item render canvas (5/6 portrait); the
	   monster modifier below swaps it to 5/4 landscape. */
	border-radius: 0;
	overflow: visible;
	background-color: var(--surface-sunken);
	display: grid;
	place-items: center;
	width: min(75vw, calc(75vh * 5 / 6));
	aspect-ratio: 5 / 6;
}
.appearance-slideshow--monster .appearance-slideshow__frame {
	width: min(75vw, calc(75vh * 5 / 4));
	aspect-ratio: 5 / 4;
}
/* .modal__panel--wide's 680px comment-thread cap would otherwise clip the
   75vw frame target above on wide screens. */
.appearance-slideshow.modal__panel--wide {
	max-width: 94vw;
}
.appearance-slideshow__frame img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: contain;
}
.appearance-slideshow__nav {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	z-index: 4;
	width: 38px;
	height: 38px;
	border-radius: 999px;
	border: none;
	color: #fff;
	background: rgba(20, 26, 33, 0.42);
	-webkit-backdrop-filter: blur(10px);
	backdrop-filter: blur(10px);
	display: grid;
	place-items: center;
	cursor: pointer;
	box-shadow: none;
	transition: background 0.15s ease;
}
.appearance-slideshow__nav[hidden] {
	/* JS sets .hidden on single-photo galleries; author display:grid above
	   outranks the UA [hidden]{display:none}, so restore it explicitly. */
	display: none;
}
.appearance-slideshow__nav[data-slideshow-prev] {
	left: 10px;
}
.appearance-slideshow__nav[data-slideshow-next] {
	right: 10px;
}
.appearance-slideshow__nav:hover {
	background: rgba(20, 26, 33, 0.62);
}
/* The global `button:active { transform: translateY(1px) }` press effect
   (app.css general button rules) outranks the plain .appearance-slideshow__nav
   class on specificity and clobbers -- not composes with -- the translateY(-50%)
   this button depends on for vertical centering. Without this override, the
   button jumps ~20px down the instant it's pressed, so mouseup lands on
   whatever's now under the stale cursor position instead of the button, and
   the click never registers. Re-assert the centering, composed with the same
   1px press nudge. */
.appearance-slideshow__nav:active {
	transform: translateY(calc(-50% + 1px));
}
@media (prefers-reduced-motion: reduce) {
	.appearance-slideshow__nav {
		transition: none;
	}
}
@media (max-width: 400px) {
	.appearance-slideshow__nav {
		width: 34px;
		height: 34px;
	}
	.appearance-slideshow__nav[data-slideshow-prev] {
		left: 6px;
	}
	.appearance-slideshow__nav[data-slideshow-next] {
		right: 6px;
	}
}
.appearance-slideshow__dots {
	display: flex;
	justify-content: center;
	gap: 6px;
	padding: 12px 14px 12px;
}
.appearance-slideshow__dot {
	width: 6px;
	height: 6px;
	padding: 0;
	border: 0;
	border-radius: 999px;
	background: rgba(255, 255, 255, 0.35);
	cursor: pointer;
}
.appearance-slideshow__dot.is-active {
	width: 16px;
	background: var(--accent-deep);
}
.appearance-slideshow__credit {
	text-align: center;
	font-size: 11.5px;
	color: rgba(255, 255, 255, 0.7);
	padding: 0 14px 14px;
}
.appearance-slideshow__credit a {
	color: #fff;
}
.appearance-slideshow__admin {
	text-align: center;
	padding: 0 14px 16px;
}
.appearance-slideshow__reorder {
	font-size: 11.5px;
	color: rgba(255, 255, 255, 0.7);
	text-decoration: underline;
	cursor: pointer;
	margin-right: 14px;
}
.appearance-slideshow__reorder:hover {
	color: #fff;
}
.appearance-slideshow__delete {
	font-size: 11.5px;
	color: var(--danger-ink);
	text-decoration: underline;
	cursor: pointer;
}
.appearance-slideshow__delete:hover {
	color: #8a3a4c;
}

/* ---------------------------------------------------------------- *
 * Home event carousel
 *
 * The artwork is staff-uploaded and arbitrary, so overlaid text can never
 * assume anything about what is behind it -- hence the scrim. 540x80 is a
 * ratio (6.75:1), not a size: the banner is fluid.
 * ---------------------------------------------------------------- */
.event-carousel {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	justify-content: flex-start;
	align-items: flex-start;
	width: 75%;
	margin: 1.5rem 0;
}

.event-carousel__header {
	display: flex;
	width: 100%;
	flex-direction: row;
	justify-content: space-between;
	align-items: center;
}

.event-carousel__title {
	font-size: 1.2rem;
	line-height: 1;
}

.event-carousel__section-title {
	display: flex;
	flex-direction: row;
	gap: 0.5rem;
	align-items: center;
	justify-content: flex-start;
	color: var(--ink);
}

.event-carousel__event-icon {
	font-size: 1.5rem;
	color: var(--accent);
	font-weight: 800;
}

.event-carousel__event-notice-section {
	display: flex;
	gap: 1rem;
	padding: 0.3rem 0;
	width: 100%;
	justify-content: flex-start;
	align-items: center;
	border-radius: 0.7rem;
	border: 10px;
	background-color: transparent;
	color: white;
}

.event-notice-section__globe-icon {
	font-size: 1.5rem;
}

.event-notice-section__text {
	font-size: 1.2rem;
}

.event-notice-section__jump-icon {
	margin-left: auto;
	font-weight: 600;
}

.event-notice-section__jump-icon-btn:hover {
	background-color: var(--ink);
}

.event-notice-section__jump-icon {
	font-size: 1.5rem;
	font-weight: 800;
}

.event-carousel__viewport {
	position: relative;
	width: 100%;
	border-radius: 18px;
	overflow: hidden;
}

.event-slide {
	position: relative;
	display: none;
}

.event-slide.is-active {
	position: relative;
	display: flex;
	height: 45vh;
	background: linear-gradient(0deg, var(--ink-3), var(--ink));
	padding: 1rem;
	flex-direction: row;
	gap: 2rem;
	justify-content: space-between;
	align-items: center;
}

.event-card__img-container {
	height: 100%;
	width: 100%;
	border-radius: 18px;
	overflow: hidden;
}

.event-slide__art {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.event-card__info-container {
	display: flex;
	width: 40%;
	padding-top: 2rem;
	padding-right: 1rem;
	height: 100%;
	flex-direction: column;
	gap: 1rem;
	align-items: flex-start;
	justify-content: space-between;
}

.event-card__dates {
	color: white;
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	justify-content: flex-start;
	align-items: flex-start;
	width: 100%;
}

.event-card__dates__header {
	display: flex;
	flex-direction: row;
	justify-content: flex-start;
	gap: 0.7rem;
	font-size: 1rem;
	font-weight: 600;
	width: auto;
}

.event-card__dates__content {
	display: flex;
	width: 100%;
	flex-direction: row;
	justify-content: space-between;
	align-items: center;
}

.event-card__dates-text {
	font-weight: 600;
	font-size: 1.5rem;
}

.event-card__right-bracket {
	font-size: 0.7rem;
	font-weight: 400;
}

.event-card__dates-text span {
	font-size: 1rem;
}

.event-card__timer {
	width: 100%;
	display: flex;
	flex-direction: column;
	gap: 0.8rem;
	justify-content: center;
	align-items: flex-start;
}

.event-card__timer__header {
	display: flex;
	width: 100%;
	flex-direction: row;
	gap: 0.5rem;
}

.timer__header__text {
	font-weight: 600;
	font-size: 1rem;
	color: white;
}

.timer__header__icon {
	font-size: 1rem;
	font-weight: 800;
	color: white;
}

.event-card__timer__tray {
	display: flex;
	gap: 0.5rem;
	flex-direction: row;
}

.time-box {
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-items: center;
	border: 4px solid var(--ink-2);
	border-radius: 5px;
	height: 5.5rem;
	width: 5.5rem;
	color: white;
	padding: 0.5rem;
}

.time-box__value {
	font-weight: 600;
	color: var(--accent);
	font-size: 2.1rem;
}

.time-box__label {
	color: white;
}

.event-card__items-btn {
	display: flex;
	flex-direction: row;
	justify-content: center;
	align-items: center;
	gap: 1rem;
	width: 65%;
	height: 4rem;
	padding: 0.5rem;
	font-size: 1.1rem;
	font-weight: 600;
	border: 3px solid var(--accent);
	background: linear-gradient(0deg, var(--accent-hover), var(--accent));
	transition: filter 0.2s ease;
}

.event-card__items-btn:hover {
	display: flex;
	flex-direction: row;
	justify-content: center;
	align-items: center;
	gap: 1rem;
	width: 65%;
	height: 4rem;
	padding: 0.5rem;
	font-size: 1.1rem;
	font-weight: 600;
	border: 3px solid var(--accent);
	background: linear-gradient(0deg, var(--accent-hover), var(--accent));
	transition: filter 0.2s ease;
	filter: brightness(0.85);
}

.event-slide__body {
	display: flex;
	align-items: center;
	gap: 14px;
}

.event-slide__period {
	margin: 0;
	font-size: 12.5px;
	font-weight: 600;
}

.event-slide__tz {
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.04em;
	opacity: 0.7;
}

.event-slide__countdown {
	display: flex;
	align-items: baseline;
	gap: 6px;
	margin: 0;
}

.event-slide__countdown-label {
	font-size: 10.5px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	opacity: 0.75;
}

.event-slide__countdown-value {
	font-family: "Space Mono", monospace;
	font-size: 14px;
	font-weight: 700;
	font-variant-numeric: tabular-nums;
}

.event-slide__cta {
	margin-left: auto;
	flex: none;
	padding: 7px 14px;
	border-radius: 9px;
	background: var(--accent);
	color: #fff;
	font-size: 13px;
	font-weight: 600;
	text-decoration: none;
	box-shadow: 0 2px 0 var(--accent-deep);
}

.event-slide__cta:hover {
	background: var(--accent-deep);
}

.event-carousel__arrow,
.event-carousel__dots {
	display: none;
}

.event-carousel.has-many .event-carousel__arrow,
.event-carousel.has-many .event-carousel__dots {
	display: flex;
}

.event-carousel__arrow {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	z-index: 2;
	align-items: center;
	justify-content: center;
	width: 30px;
	height: 30px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: rgba(27, 34, 43, 0.55);
	color: #fff;
	font-size: 18px;
	line-height: 1;
	cursor: pointer;
}

.event-carousel__arrow:hover {
	background: rgba(27, 34, 43, 0.8);
}
.event-carousel__arrow--prev {
	left: 10px;
}
.event-carousel__arrow--next {
	right: 10px;
}

.event-carousel__dots {
	justify-content: center;
	gap: 6px;
	margin-top: 8px;
}

.event-carousel__dot {
	width: 7px;
	height: 7px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: var(--hairline);
	cursor: pointer;
}

.event-carousel__dot.is-active {
	background: var(--accent-deep);
}

/* Desktop: the controls sit on top of the artwork, over a scrim.
   Mobile keeps them in a bar below -- see the note further down. */
@media (min-width: 820px) {
	.event-slide {
		position: relative;
	}

	.event-slide__body {
		position: absolute;
		inset: 0;
		padding: 0 18px 0 42%;
		color: #fff;
		/* Left-to-right scrim: the art's focal point is usually on the left, so
       the copy side is the one that gets darkened. */
		background: linear-gradient(
			90deg,
			rgba(11, 15, 20, 0) 0%,
			rgba(11, 15, 20, 0.55) 32%,
			rgba(11, 15, 20, 0.82) 60%
		);
	}

	.event-slide__countdown-value {
		font-size: 16px;
	}
}

/* Mobile: 6.75:1 at ~360px wide is only ~53px tall, which cannot hold an
   overlaid period, countdown and button at any font size. So the slide stacks:
   art on top at its true ratio, controls in a solid bar beneath it. */
@media (max-width: 819px) {
	.event-carousel {
		gap: 0.5rem;
		width: 100%;
	}

	.event-carousel__title {
		font-size: 0.7rem;
	}

	.event-carousel__event-icon {
		font-size: 0.8rem;
	}

	.view-all-btn {
		font-size: 0.5rem;
		padding: 0.5rem;
	}

	.event-slide.is-active {
		padding: 0;
		height: auto;
		flex-direction: column;
		gap: 0.5rem;
		width: 100%;
		background: transparent;
	}

	.event-card__info-container {
		width: 100%;
		padding: 0;
		display: grid;
		grid-template-columns: repeat(2, 50%);
		grid-template-rows: repeat(2, 50%);
		gap: 0.3rem;
		height: 8.8rem;
		min-height: fit-content;
	}

	.event-card__dates {
		background: linear-gradient(0deg, var(--ink-2), var(--ink));
		padding: 0.4rem;
		border-radius: 8px;
		border: 2px solid var(--ink-2);
		height: 4.5rem;
		gap: 0.5rem;
	}

	.event-card__dates__header {
		font-size: 0.5rem;
		gap: 0.5rem;
	}

	.event-card__dates__content {
		gap: 1rem;
	}

	.event-card__dates-text {
		font-size: 0.7rem;
	}

	.event-card__dates-text span {
		font-size: 0.4rem;
	}

	.event-card__right-bracket {
		font-size: 0.6rem;
		font-weight: 600;
	}

	.event-card__timer {
		background: linear-gradient(0deg, var(--ink-2), var(--ink));
		padding: 0.4rem;
		border-radius: 8px;
		height: 4.5rem;
		border: 2px solid var(--ink-2);
		justify-content: flex-start;
		gap: 0.5rem;
	}

	.event-card__timer__header {
		gap: 0.5rem;
		align-items: center;
		font-size: 0.5rem;
	}

	.timer__header__icon {
		font-size: 0.5rem;
	}

	.timer__header__text {
		font-size: 0.5rem;
	}

	.event-card__timer__tray {
		gap: 0.3rem;
		width: 100%;
		justify-content: flex-start;
	}

	.time-box {
		height: 2rem;
		width: 2rem;
		border: 2px solid var(--ink-muted);
		padding: 0;
	}

	.time-box__value {
		font-size: 0.9rem;
	}

	.time-box__label {
		font-size: 0.3rem;
	}

	.event-card__items-btn {
		height: 3rem;
		width: 100%;
		font-size: 0.7rem;
	}

	.event-carousel__event-notice-section {
		background-color: var(--ink-2);
		padding: 1rem;
		gap: 0.5rem;
		height: 3rem;
		width: 100%;
	}

	.event-notice-section__globe-icon {
		font-size: 0.8rem;
	}

	.event-notice-section__text {
		font-size: 0.5rem;
	}

	.event-notice-section__jump-icon {
		font-size: 0.8rem;
	}

	.event-slide__body {
		flex-wrap: wrap;
		gap: 8px 12px;
		padding: 10px 12px;
		background: var(--surface);
		border-top: 1px solid var(--hairline);
		color: var(--ink);
	}

	.event-slide__cta {
		width: 100%;
		margin-left: 0;
		text-align: center;
	}

	#mod-section-held-appearance {
		grid-template-columns: repeat(1, 100%);
	}
}

@media (prefers-reduced-motion: reduce) {
	.event-slide {
		transition: none;
	}
}

/* ---------------- Profile pictures (avatars) ---------------- */
/* Every avatar is a frame: the classed element (.avatar, .trust-avatar, …)
   carries shape + size and clips two absolutely-positioned layers — an
   optional background image, and the character picture (or letter) on top.
   Character pictures are transparent, so the background shows through. With no
   layers it degrades to the classed element's own bg-colour + centred letter. */
.avatar-frame {
	position: relative;
	overflow: hidden;
}
.avatar-frame[data-zoom] {
	cursor: zoom-in;
}
.avatar-bg,
.avatar-img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	border-radius: inherit;
	display: block;
}
.avatar-bg {
	z-index: 0;
}
.avatar-img {
	z-index: 1;
}
.list-row__avatar {
	width: 30px;
	height: 30px;
	border-radius: 50%;
	background: var(--accent-tint);
	color: var(--accent-deep);
	display: grid;
	place-items: center;
	font-weight: 700;
	font-size: 12px;
	flex-shrink: 0;
	margin-right: 10px;
}

/* Pencil-edit button overlapping the profile avatar. */
.avatar-edit {
	position: relative;
	flex-shrink: 0;
}
.avatar-edit__btn {
	position: absolute;
	right: -4px;
	bottom: -4px;
	width: 20px;
	height: 20px;
	border-radius: 50%;
	border: 2px solid var(--surface);
	background: var(--accent);
	display: grid;
	place-items: center;
	cursor: pointer;
	padding: 0;
	/* Sit above the avatar frame's layers (avatar-img is z-index:1), or the
	   picture paints over this corner and swallows the click. */
	z-index: 2;
}
.avatar-edit__btn:hover {
	background: var(--accent-deep);
}

/* ---- Picker modal ---- */
.avatar-picker__grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
	gap: 12px;
	margin-top: 8px;
}
.avatar-picker__error {
	color: var(--danger, #c0392b);
}
.avatar-choice {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
	padding: 6px;
	border: 1px solid transparent;
	border-radius: 12px;
	background: none;
	cursor: pointer;
	font-family: inherit;
}
.avatar-choice:hover {
	background: var(--accent-tint);
}
.avatar-choice.is-current {
	border-color: var(--accent);
	background: var(--accent-tint);
}
.avatar-choice__thumb {
	position: relative;
	width: 64px;
	height: 64px;
	border-radius: 50%;
	overflow: hidden;
	background: var(--accent-tint);
	display: grid;
	place-items: center;
}
.avatar-choice__thumb img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}
.avatar-choice__thumb--letter {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 26px;
	color: var(--accent-deep);
}
.avatar-choice__name {
	font-size: 11px;
	color: var(--ink-muted-2);
	text-align: center;
	max-width: 76px;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.avatar-choice.is-locked {
	cursor: not-allowed;
}
.avatar-choice.is-locked .avatar-choice__thumb img {
	filter: grayscale(1) brightness(0.7);
}
.avatar-choice__lock {
	position: absolute;
	inset: 0;
	display: grid;
	place-items: center;
	background: rgba(27, 34, 43, 0.4);
}
.avatar-choice__tip {
	position: absolute;
	bottom: calc(100% + 6px);
	left: 50%;
	transform: translateX(-50%);
	background: var(--ink, #1b222b);
	color: #fff;
	font-size: 11px;
	line-height: 1.3;
	padding: 6px 9px;
	border-radius: 8px;
	width: max-content;
	max-width: 180px;
	text-align: center;
	opacity: 0;
	visibility: hidden;
	transition: opacity 0.12s;
	z-index: 5;
	pointer-events: none;
}
.avatar-choice.is-locked:hover .avatar-choice__tip {
	opacity: 1;
	visibility: visible;
}

/* ---- Picker: live preview + tabs ---- */
.picker-preview {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
	margin-bottom: 14px;
}
.picker-preview__avatar {
	width: 72px;
	height: 72px;
	border-radius: 16px;
}
.picker-preview__hint {
	font-size: 11px;
	color: var(--ink-faint);
}
.picker-tabs {
	display: flex;
	gap: 4px;
	padding: 3px;
	background: var(--surface-2, var(--accent-tint));
	border-radius: 10px;
	margin-bottom: 12px;
}
.picker-tab {
	flex: 1;
	border: none;
	background: none;
	padding: 7px 10px;
	border-radius: 8px;
	font-family: inherit;
	font-weight: 600;
	font-size: 12.5px;
	color: var(--ink-muted-2);
	cursor: pointer;
}
.picker-tab.is-active {
	background: var(--surface);
	color: var(--ink);
	box-shadow: var(--shadow-soft, 0 1px 2px rgba(0, 0, 0, 0.08));
}

/* ---- Admin catalog list ---- */
.avatar-admin-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
	gap: 14px;
	width: 100%;
}
.avatar-admin-card {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 10px;
	border: 1px solid var(--hairline);
	border-radius: var(--r-btn);
}
.avatar-admin-card.is-inactive {
	opacity: 0.55;
}
.avatar-admin-card__img {
	width: 52px;
	height: 52px;
	border-radius: 50%;
	object-fit: cover;
	flex-shrink: 0;
}
.avatar-admin-card__body {
	flex: 1;
	min-width: 0;
}
.avatar-admin-card__name {
	font-weight: 700;
	font-size: 14px;
	color: var(--ink);
}
.avatar-admin-card__meta {
	font-size: 12px;
	color: var(--ink-muted-2);
	margin-top: 2px;
}
.avatar-admin-card__actions {
	display: flex;
	flex-direction: column;
	gap: 4px;
	align-items: flex-end;
}
.link-btn--danger {
	color: var(--danger, #c0392b);
	background: none;
	border: none;
	cursor: pointer;
	padding: 0;
	font: inherit;
}

/* ---- Admin upload cropper ---- */
.avatar-cropper {
	display: flex;
	align-items: center;
	gap: 16px;
}
.avatar-cropper__preview {
	width: 96px;
	height: 96px;
	border-radius: 50%;
	object-fit: cover;
	background: var(--accent-tint);
	display: grid;
	place-items: center;
	font-size: 11px;
	color: var(--ink-muted-2);
	flex-shrink: 0;
}
.avatar-cropper__controls {
	flex: 1;
	min-width: 0;
}
.avatar-cropper__controls input[type="file"] {
	display: none;
}
.field--check {
	flex-direction: row;
	align-items: center;
	gap: 8px;
}
.field__hint--error {
	color: var(--danger, #c0392b);
}

/* --------------------------------------------------------------------- */
/* Trait cards (/items/?type=trait) — tier picker + stack stepper.        */
/* Reuses .card/.stat-row/.stat-list from the item card; only the trait-  */
/* specific controls are new. See web/partials/trait_card.html.           */
/* --------------------------------------------------------------------- */
.trait-card__head {
	align-items: flex-start;
	flex-direction: column;
	gap: 6px;
	cursor: default;
}
.trait-badges {
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
}
.trait-badge {
	font-size: 10px;
	font-weight: 700;
	letter-spacing: 0.02em;
	text-transform: uppercase;
	padding: 2px 7px;
	border-radius: var(--r-pill);
	background: var(--surface-alt);
	color: var(--ink-muted);
}
.trait-badge--slot {
	background: var(--accent-tint);
	color: var(--accent-deep);
}
.trait-badge--cond {
	background: var(--warn-bg);
	color: var(--warn-ink);
}
.trait-badge--stack {
	background: var(--material-bg);
	color: var(--material-ink);
}
.trait-card__desc,
.trait-card__note {
	font-size: 12.5px;
	line-height: 1.45;
	color: var(--ink-2);
	margin: 0 0 10px;
}
.trait-card__note {
	color: var(--ink-muted);
	font-style: italic;
}
/* raffy's `note` -- the scaling formula behind the effect text. Monospace
   because it's an expression (+TEC/(6−T)), not prose. */
.trait-card__formula {
	font-family: var(--font-mono);
	font-size: 11px;
	line-height: 1.45;
	color: var(--ink-muted-2);
	background: var(--surface-sunken);
	border-radius: 6px;
	padding: 5px 8px;
	margin: -4px 0 10px;
	overflow-wrap: anywhere;
}

/* Tier picker: a segmented control, one button per allowed tier. */
.trait-tiers {
	display: flex;
	gap: 4px;
	padding: 3px;
	margin-bottom: 10px;
	background: var(--surface-sunken);
	border: 1px solid var(--hairline-soft);
	border-radius: var(--r-btn);
}
.trait-tier-btn {
	flex: 1;
	min-width: 0;
	padding: 5px 0;
	font-family: var(--font-mono);
	font-size: 12px;
	font-weight: 700;
	color: var(--ink-muted);
	background: none;
	border: none;
	border-radius: 6px;
	cursor: pointer;
	transition: background 0.14s, color 0.14s;
}
.trait-tier-btn:hover {
	background: var(--surface);
	color: var(--ink);
}
.trait-tier-btn.is-active {
	background: var(--accent-deep);
	color: #fff;
}
.trait-tier-btn:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 1px;
}

/* Stack stepper — only rendered for stackable traits. */
.trait-stack {
	display: flex;
	align-items: center;
	gap: 6px;
	margin-bottom: 8px;
	padding: 5px 8px;
	background: var(--surface-sunken);
	border-radius: var(--r-btn);
}
.trait-stack__label {
	font-size: 11px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.02em;
	color: var(--ink-muted-2);
	margin-right: auto;
}
.trait-stack__btn {
	width: 22px;
	height: 22px;
	display: grid;
	place-items: center;
	/* The global `button` rule sets padding: 9px 15px, which overflows a
	   22px box and shoves the glyph off-centre -- reset it here. */
	padding: 0;
	font-size: 14px;
	font-weight: 700;
	line-height: 1;
	color: var(--accent-deep);
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: 6px;
	cursor: pointer;
}
.trait-stack__btn:hover {
	background: var(--accent-tint);
}
.trait-stack__val {
	width: 42px;
	height: 22px;
	text-align: center;
	font-family: var(--font-mono);
	font-size: 12px;
	font-weight: 700;
	color: var(--ink);
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: 6px;
}
/* The spinners duplicate the −/+ buttons and crowd a 42px field. */
.trait-stack__val::-webkit-outer-spin-button,
.trait-stack__val::-webkit-inner-spin-button {
	-webkit-appearance: none;
	margin: 0;
}
.trait-stack__val {
	-moz-appearance: textfield;
	appearance: textfield;
}
.trait-stack__max {
	font-family: var(--font-mono);
	font-size: 11px;
	color: var(--ink-faint);
}

/* .trait-tier sets no display of its own, so the UA [hidden] rule applies
   as-is -- unlike .stat-row/.source, which need the explicit override
   above. Stated here so a later `display:` addition doesn't silently break
   tier switching. */
