/* The game's own fonts. fonts/minecraft-*.woff2 is Minecraft's font
   (extracted from the game by minecraft-library/font-generator; "Copyright
   Mojang AB"); Unifont is the fallback the game itself uses for characters
   its font lacks (SIL OFL 1.1 / GPLv2+ with font embedding exception).
   Converted to woff2 from the .otf originals - same glyphs, ~5x smaller.
   Browsers only download a face when the page actually uses it, so italic
   and Unifont are usually never fetched. */
@font-face {
	font-family: "Minecraft";
	src: url("fonts/minecraft-regular.woff2") format("woff2");
	font-weight: 400;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: "Minecraft";
	src: url("fonts/minecraft-bold.woff2") format("woff2");
	font-weight: 700;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: "Minecraft";
	src: url("fonts/minecraft-italic.woff2") format("woff2");
	font-weight: 400;
	font-style: italic;
	font-display: swap;
}

@font-face {
	font-family: "Minecraft";
	src: url("fonts/minecraft-bold-italic.woff2") format("woff2");
	font-weight: 700;
	font-style: italic;
	font-display: swap;
}

@font-face {
	font-family: "Unifont";
	src: url("fonts/unifont.woff2") format("woff2");
	font-display: swap;
}

/* ====================================================================== */
/* Minecraft UI kit                                                       */
/* ====================================================================== */
/* The site's chrome mimics Minecraft Java's container screens (inventory,
   chest, creative tabs, title-screen buttons). notes/UI_STYLE.md is the
   written spec for everything in this section - palette sources, which component
   uses which primitive, and why. Short version: no rounded corners, no
   blurred shadows, no color transitions; depth comes only from hard 2px
   (one texel at GUI scale 2) light/dark edges drawn with inset box-shadow.
   Component rules further down this file deliberately set no background/
   border/box-shadow/color of their own for anything listed here - the kit
   owns the look, components own layout and sizing. */

:root {
	/* Fixed Minecraft colors - identical in both themes. */
	--mc-outline: #000000;
	--btn: #6f6f6f;
	--btn-hi: #a8a8a8;
	--btn-lo: #464646;
	--btn-hover-fill: #7a7a7a;
	--btn-pressed: #4a4a4a;
	--btn-disabled: #2c2c2c;
	--btn-ink: #ffffff;
	--btn-ink-hover: #ffffa0;
	--btn-ink-active: #ffff55;
	--btn-ink-disabled: #a0a0a0;
	--danger-btn: #7a2b2b;
	--danger-btn-hi: #b24a4a;
	--danger-btn-lo: #4a1515;
	--text-shadow-color: #3f3f3f;
	--field-bg: #000000;
	--field-border: #a0a0a0;
	--field-ink: #e0e0e0;
	--field-placeholder: #8a8a8a;
	--tooltip-bg: rgba(16, 0, 16, 0.94);
	--tooltip-border-top: rgba(80, 0, 255, 0.8);
	--tooltip-border-bottom: rgba(40, 0, 127, 0.8);
	--tooltip-ink: #ffffff;
	--tooltip-ink-muted: #aaaaaa;
	--xp-green: #80ff20;
	--xp-green-shade: #3f8f10;
	/* The placement ghost's fits / doesn't-fit outline (§a green, §c red). */
	--ghost-fits: #55ff55;
	--ghost-blocked: #ff5555;
	--warn-gold: #ffaa00;
	/* White page-level text on the dirt background, same in both themes -
	   like the game's screen titles drawn straight over the menu backdrop. */
	--ink-on-bg: #ffffff;
	--ink-on-bg-muted: #aaaaaa;
	/* Text on flat building badges (pale category tint over white) - the
	   PNG export (planner/png-export.js) reads this too. */
	--badge-ink: #1a1a1a;
	/* Unifont fills in any character the Minecraft font lacks, as in-game. */
	--font-pixel: "Minecraft", "Unifont", monospace;
	/* Minecraft's panels have one texel clipped off each corner. Outlines
	   get clipped along with it, which is why focus styling in the kit
	   recolors the border instead of drawing an outline. */
	--mc-notch: polygon(
		2px 0,
		calc(100% - 2px) 0,
		100% 2px,
		100% calc(100% - 2px),
		calc(100% - 2px) 100%,
		2px 100%,
		0 calc(100% - 2px),
		0 2px
	);
	--raised: inset 2px 2px 0 var(--panel-hi), inset -2px -2px 0 var(--panel-lo);
	--raised-thick:
		inset 4px 4px 0 var(--panel-hi), inset -4px -4px 0 var(--panel-lo);
	--sunken-well:
		inset 2px 2px 0 var(--well-lo), inset -2px -2px 0 var(--well-hi);
	--sunken-slot:
		inset 2px 2px 0 var(--slot-lo), inset -2px -2px 0 var(--slot-hi);

	--bottom-bar-height: 0px;
	--topbar-height: 52px;
	--left-sidebar-width: 240px;
	--left-sidebar-collapsed-width: 44px;
	--left-sidebar-mobile-height: 120px;
	--left-sidebar-mobile-collapsed-height: 44px;
	--preview-sidebar-width: 320px;
	--preview-sidebar-collapsed-width: 44px;
	--preview-sidebar-mobile-height: 188px;
	--preview-sidebar-mobile-collapsed-height: 44px;
	/* A shape card's height (149px, see .shape-option) plus the tray's 14px
	   horizontal scrollbar - anything more is dead space taken from the map. */
	--bottom-tray-height: 163px;
}

/* Dark theme: "deepslate" - the same bevel rules built from darker stone. */
:root,
[data-theme="dark"] {
	--panel: #3a3a3a;
	--panel-hi: #5a5a5a;
	--panel-lo: #1e1e1e;
	--well: #2c2c2c;
	--well-hi: #4a4a4a;
	--well-lo: #151515;
	--slot: #232323;
	--slot-hover: #2f2f2f;
	--slot-hi: #4a4a4a;
	--slot-lo: #0a0a0a;
	--tab-off: #2a2a2a;
	--ink: #e6e6e6;
	--ink-muted: #a8a8a8;
	--ink-title: #e6e6e6;
	--link: #55ffff;
	--accent-selected: #ffff55;
	/* Map-only tokens (planner grid + PNG export). */
	--grid-bg: #1e1e1e;
	--grid-line: #3a3a3a;
	--grid-line-light: rgba(255, 255, 255, 0.08);
	/* The world background is always fully opaque, so chunk lines are
	   translucent (a solid line reads as a harsh bar over varied terrain)
	   and cell lines are stronger than the old 0.08 so they stay visible. */
	--map-grid-major: rgba(255, 255, 255, 0.3);
	--map-grid-minor: rgba(255, 255, 255, 0.18);
	--path-cell-bg: rgba(100, 180, 220, 0.4);
	--path-cell-border: rgba(60, 140, 200, 0.8);
	--path-selected-outline: rgba(180, 120, 0, 1);
	--path-bevel-light: rgba(255, 255, 255, 0.45);
	--path-bevel-dark: rgba(0, 0, 0, 0.4);
}

/* Light theme: the classic container gray. Duplicated below for
   prefers-color-scheme before lib/nav.js has set data-theme - keep in sync. */
[data-theme="light"] {
	--panel: #c6c6c6;
	--panel-hi: #ffffff;
	--panel-lo: #555555;
	--well: #bdbdbd;
	--well-hi: #ffffff;
	--well-lo: #555555;
	--slot: #8b8b8b;
	--slot-hover: #9c9c9c;
	--slot-hi: #ffffff;
	--slot-lo: #373737;
	--tab-off: #a4a4a4;
	--ink: #262626;
	--ink-muted: #4a4a4a;
	--ink-title: #404040;
	--link: #0000aa;
	--accent-selected: #d98e00;
	--grid-bg: #f2f2f2;
	--grid-line: #c8c8cf;
	--grid-line-light: rgba(0, 0, 0, 0.08);
	--map-grid-major: rgba(0, 0, 0, 0.28);
	--map-grid-minor: rgba(0, 0, 0, 0.16);
	--path-cell-bg: rgba(58, 140, 190, 0.28);
	--path-cell-border: rgba(30, 100, 150, 0.7);
	--path-selected-outline: rgba(150, 90, 0, 1);
	--path-bevel-light: rgba(255, 255, 255, 0.45);
	--path-bevel-dark: rgba(0, 0, 0, 0.4);
}

@media (prefers-color-scheme: light) {
	:root:not([data-theme]) {
		--panel: #c6c6c6;
		--panel-hi: #ffffff;
		--panel-lo: #555555;
		--well: #bdbdbd;
		--well-hi: #ffffff;
		--well-lo: #555555;
		--slot: #8b8b8b;
		--slot-hover: #9c9c9c;
		--slot-hi: #ffffff;
		--slot-lo: #373737;
		--tab-off: #a4a4a4;
		--ink: #262626;
		--ink-muted: #4a4a4a;
		--ink-title: #404040;
		--link: #0000aa;
		--accent-selected: #d98e00;
		--grid-bg: #f2f2f2;
		--grid-line: #c8c8cf;
		--grid-line-light: rgba(0, 0, 0, 0.08);
		--map-grid-major: rgba(0, 0, 0, 0.28);
		--map-grid-minor: rgba(0, 0, 0, 0.16);
		--path-cell-bg: rgba(58, 140, 190, 0.28);
		--path-cell-border: rgba(30, 100, 150, 0.7);
		--path-selected-outline: rgba(150, 90, 0, 1);
		--path-bevel-light: rgba(255, 255, 255, 0.45);
		--path-bevel-dark: rgba(0, 0, 0, 0.4);
	}
}

* {
	box-sizing: border-box;
}

/* The game's darkened dirt menu backdrop, the same in both themes.
   images/ui/dirt-background.png is one seamless 64px tile (16 texels at 4px
   each). It's drawn on a fixed pseudo-element so image-rendering: pixelated
   keeps it crisp on high-DPI screens without being inherited by real
   content images (building previews), and so it stays put while the
   utility pages scroll. #433021 is the tile's average color, shown until
   it loads. */
body {
	margin: 0;
	font-family: var(--font-pixel);
	background: #433021;
	color: var(--ink-on-bg);
}

/* The JS hides things with the `hidden` attribute everywhere, but the UA
   stylesheet's display:none for it loses to ANY author rule that sets
   display (e.g. a component's display:flex) - which left a dead "?" button,
   the background controls, the Front/Top-down toggle and the walls-mode
   Width field visible while "hidden". One rule for all of them instead of
   a per-component [hidden] patch each time. */
[hidden] {
	display: none !important;
}

body::before {
	content: "";
	position: fixed;
	inset: 0;
	z-index: -1;
	background: url("images/ui/dirt-background.png") 0 0 / 64px 64px;
	image-rendering: pixelated;
}

code {
	font-family: inherit;
	color: var(--ink-title);
}

/* --- Panel: raised gray container with a black outline ----------------- */
/* Screen-edge panels: thick bevel, no corner notch (they meet the edges). */
.app-nav,
.left-sidebar,
.preview-sidebar,
.bottom-bar {
	background: var(--panel);
	color: var(--ink);
	border: 2px solid var(--mc-outline);
	box-shadow: var(--raised-thick);
}

/* Floating panels: thinner bevel, notched corners. */
.shortcuts-modal,
.action-menu,
.path-action-menu,
.inspector-card,
.inspector-citizen-card,
.world-viewer-goto,
.debug-material-item,
.debug-biome-item,
.inspector-upload__zone,
.inspector-help,
.inspector-notice,
.inspector-raw,
.inspector-table {
	background: var(--panel);
	color: var(--ink);
	border: 2px solid var(--mc-outline);
	box-shadow: var(--raised);
	clip-path: var(--mc-notch);
}

/* Panel titles are flat dark gray with no shadow, like "Crafting". */
.app-nav__brand,
.left-sidebar__title,
.preview-sidebar__title,
.preview-card__name,
.inspector-card__title,
.inspector-citizen-card__name,
.shortcuts-modal__header h2 {
	font-family: var(--font-pixel);
	color: var(--ink-title);
}

/* Everything inherits the pixel font from body; these are listed only
   because form controls and a few absolutely-positioned labels don't
   inherit it by default. */
.left-sidebar__section-title,
.plan-check__group-title,
.plan-check__item--fix,
.plan-check__title,
.cost-card__section-title,
.shortcuts-modal__group h3,
.world-bg-controls__row,
.left-sidebar__plan-name,
.inspector-stat-tile__label,
.inspector-stat-tile__value,
.inspector-chip,
.shape-label,
.shape-dimensions,
.placed-badge,
.preview-badge,
.preview-card__topdown-label {
	font-family: var(--font-pixel);
}

/* On desktop the sidebars are columns wedged between the nav bar and the
   bottom bar. Their own top/bottom outline and bottom shadow edge would
   double up against those bars' edges (and stop short of the map, which has
   none), so they drop them and read as one continuous frame. Below 1025px
   they become horizontal bands with all four edges again. */
@media (min-width: 1025px) {
	.left-sidebar,
	.preview-sidebar {
		border-top: none;
		border-bottom: none;
		box-shadow:
			inset 4px 0 0 var(--panel-hi),
			inset -4px 0 0 var(--panel-lo);
	}
}

/* --- Well: sunken group inside a panel --------------------------------- */
.left-sidebar__content,
.preview-card,
.cost-card,
.grid-goto-controls,
.inspector-stat-tile {
	background: var(--well);
	color: var(--ink);
	border: none;
	box-shadow: var(--sunken-well);
}

.inspector-upload__zone.is-dragover {
	border-color: var(--btn-ink);
}

/* --- Raised piece: a small bevelled block sitting inside a well -------- */
.plan-check__item,
.left-sidebar__plan-row,
.cost-card__material,
.inspector-chip {
	background: var(--panel);
	color: var(--ink);
	border: none;
	box-shadow: var(--raised);
}

/* Warnings get a gold edge (§6) down the left side. */
.plan-check__item--warn {
	box-shadow:
		inset 4px 0 0 var(--warn-gold),
		var(--raised);
}

/* A clickable raised piece (a Plan Check warning with a fix) hovers and
   focuses like a button: a white outline, nothing else. */
.plan-check__item--fix:is(:hover, :focus-visible) {
	outline: 2px solid var(--btn-ink);
	outline-offset: 0;
}

.plan-check__fix {
	color: var(--link);
}

/* --- Slot: sunken item slot -------------------------------------------- */
.shape-preview,
.preview-card__image-wrap,
.world-viewer-canvas-wrap,
.debug-material-item__icon-wrap,
.cost-card__material-icon--placeholder {
	background: var(--slot);
	border: none;
	box-shadow: var(--sunken-slot);
}

/* Shape tray: each card is a sunken well holding its picture slot, name
   and footprint. Hovering draws the game's translucent white overlay over
   the slot's contents. */
.shape-option {
	background: var(--well);
	border: none;
	box-shadow: var(--sunken-well);
	color: var(--ink);
}

.shape-option:hover .shape-preview::after {
	content: "";
	position: absolute;
	inset: 4px;
	background: rgba(255, 255, 255, 0.3);
}

/* Hotbar selection: a black-then-white frame around the slot. */
.shape-option.is-selected .shape-preview {
	box-shadow:
		0 0 0 2px var(--mc-outline),
		0 0 0 4px var(--btn-ink),
		var(--sunken-slot);
}

.shape-option:focus-visible {
	outline: 2px dashed var(--ink);
	outline-offset: -2px;
}

.shape-dimensions {
	color: var(--ink-muted);
}

/* --- Button: stone, black outline, white shadowed label ---------------- */
/* :where() keeps this at zero specificity so each component's own sizing
   (padding, font-size) still applies on top of it. */
:where(
	.app-nav__theme-toggle,
	.app-nav__shortcuts-toggle,
	.app-nav__link,
	.shortcuts-modal__close,
	.left-sidebar__toggle,
	.left-sidebar__button,
	.left-sidebar__plan-action,
	.preview-sidebar__toggle,
	.preview-card__view-btn,
	.cost-card__level-button,
	.grid-zoom-controls button,
	.grid-goto-controls button,
	.subtab,
	.path-type-swatch,
	.path-controls__erase-toggle,
	.number-stepper__button,
	.action-menu button,
	.path-action-menu button,
	.inspector-upload__button,
	.world-viewer-goto button,
	.world-viewer-zoom-controls button,
	.shortcuts-modal kbd
) {
	font: inherit;
	font-family: var(--font-pixel);
	background: var(--btn);
	color: var(--btn-ink);
	border: 2px solid var(--mc-outline);
	box-shadow:
		inset 2px 2px 0 var(--btn-hi),
		inset -2px -2px 0 var(--btn-lo);
	text-shadow: 1px 1px 0 var(--text-shadow-color);
	clip-path: var(--mc-notch);
}

/* Hover/focus: the fill stays put - only the outline goes white and the
   label pale yellow. Focus reuses it because the notch clips outlines. */
:is(
		.app-nav__theme-toggle,
		.app-nav__shortcuts-toggle,
		.app-nav__link,
		.shortcuts-modal__close,
		.left-sidebar__toggle,
		.left-sidebar__button,
		.left-sidebar__plan-action,
		.preview-sidebar__toggle,
		.preview-card__view-btn,
		.cost-card__level-button,
		.grid-zoom-controls button,
		.grid-goto-controls button,
		.subtab,
		.path-type-swatch,
		.path-controls__erase-toggle,
		.number-stepper__button,
		.action-menu button,
		.path-action-menu button,
		.inspector-upload__button,
		.world-viewer-goto button,
		.world-viewer-zoom-controls button
	):is(:hover, :focus-visible):not(:disabled) {
	border-color: var(--btn-ink);
	color: var(--btn-ink-hover);
	outline: none;
}

/* Pressed (mouse down) and toggled-on (.is-active): the bevel flips. */
:is(
		.app-nav__theme-toggle,
		.app-nav__shortcuts-toggle,
		.app-nav__link,
		.shortcuts-modal__close,
		.left-sidebar__toggle,
		.left-sidebar__button,
		.left-sidebar__plan-action,
		.preview-sidebar__toggle,
		.preview-card__view-btn,
		.cost-card__level-button,
		.grid-zoom-controls button,
		.grid-goto-controls button,
		.subtab,
		.path-type-swatch,
		.path-controls__erase-toggle,
		.number-stepper__button,
		.action-menu button,
		.path-action-menu button,
		.inspector-upload__button,
		.world-viewer-goto button,
		.world-viewer-zoom-controls button
	):is(:active, .is-active):not(:disabled) {
	background: var(--btn-pressed);
	box-shadow:
		inset 2px 2px 0 var(--btn-lo),
		inset -2px -2px 0 var(--btn-pressed);
}

/* Toggled on: pressed in, with a yellow (§e) label. */
:is(
	.app-nav__link,
	.preview-card__view-btn,
	.cost-card__level-button,
	.subtab,
	.path-type-swatch,
	.path-controls__erase-toggle
).is-active {
	color: var(--btn-ink-active);
}

:is(.left-sidebar__button, .left-sidebar__plan-action, .number-stepper__button):disabled {
	background: var(--btn-disabled);
	color: var(--btn-ink-disabled);
	box-shadow: none;
	text-shadow: none;
	cursor: not-allowed;
}

/* Destructive actions turn the whole button red - red text on the gray
   stone fill has almost no contrast. */
:is(
	.left-sidebar__button--danger,
	.left-sidebar__plan-action--danger,
	.action-menu button[data-action="delete"],
	.path-action-menu button[data-path-action="delete"]
) {
	background: var(--danger-btn);
	box-shadow:
		inset 2px 2px 0 var(--danger-btn-hi),
		inset -2px -2px 0 var(--danger-btn-lo);
}

/* --- Tab: creative-inventory tabs -------------------------------------- */
/* Unselected tabs are darker and sit lower; the selected tab is panel-
   colored and stands 4px taller, over the line the row sits on. */
.tab-bar {
	align-items: flex-end;
	gap: 2px;
	box-shadow: inset 0 -2px 0 var(--mc-outline);
}

.tab {
	font: inherit;
	font-family: var(--font-pixel);
	background: var(--tab-off);
	color: var(--ink-title);
	border: 2px solid var(--mc-outline);
	border-bottom: none;
	box-shadow:
		inset 2px 2px 0 var(--panel-hi),
		inset -2px 0 0 var(--panel-lo);
}

.tab:hover,
.tab:focus-visible {
	color: var(--ink);
	outline: none;
}

.tab.is-active {
	background: var(--panel);
	color: var(--ink);
	padding-bottom: 11px;
}

/* --- Text field / select: black box, gray border, white on focus ------- */
:where(
	.left-sidebar__text-input,
	.grid-goto-controls input[type="number"],
	.number-stepper__input,
	.shape-search-input,
	.style-select,
	.inspector-picker__select,
	.world-viewer-goto input[type="number"],
	.debug-search
) {
	font: inherit;
	font-family: var(--font-pixel);
	background: var(--field-bg);
	color: var(--field-ink);
	border: 2px solid var(--field-border);
	/* Native select arrows/number spinners render light-on-dark. */
	color-scheme: dark;
}

:is(
	.left-sidebar__text-input,
	.world-bg-controls__offsets input[type="number"],
	.grid-goto-controls input[type="number"],
	.number-stepper__input,
	.shape-search-input,
	.style-select,
	.inspector-picker__select,
	.world-viewer-goto input[type="number"],
	.debug-search
):focus {
	border-color: var(--btn-ink);
	outline: none;
}

:is(
	.left-sidebar__text-input,
	.shape-search-input,
	.debug-search
)::placeholder {
	color: var(--field-placeholder);
}

/* --- Checkbox: a small slot with a white pixel tick -------------------- */
input[type="checkbox"] {
	appearance: none;
	-webkit-appearance: none;
	flex-shrink: 0;
	width: 18px;
	height: 18px;
	margin: 0;
	background: var(--slot);
	box-shadow: var(--sunken-slot);
	cursor: pointer;
}

input[type="checkbox"]:checked {
	background:
		url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 7 7' shape-rendering='crispEdges'><path fill='%23fff' d='M5 1h1v1h-1zM4 2h1v1h-1zM1 3h1v1h-1zM3 3h1v1h-1zM2 4h1v1h-1z'/></svg>")
			center / 14px 14px no-repeat,
		var(--slot);
}

input[type="checkbox"]:focus-visible {
	outline: 2px solid var(--btn-ink);
	outline-offset: 1px;
}

/* --- Tooltip: the purple-framed item tooltip --------------------------- */
.world-viewer-tooltip,
.sidebar-tooltip,
.hover-coords,
.map-compass,
.placement-ghosts__message {
	font-family: var(--font-pixel);
	color: var(--tooltip-ink);
	border: 2px solid transparent;
	background:
		linear-gradient(var(--tooltip-bg), var(--tooltip-bg)) padding-box,
		linear-gradient(var(--tooltip-border-top), var(--tooltip-border-bottom))
			border-box;
	box-shadow: 0 0 0 2px var(--tooltip-bg);
	text-shadow: 1px 1px 0 var(--text-shadow-color);
}

.hover-coords__biome {
	color: var(--tooltip-ink-muted);
}

/* --- Meter: the XP bar ------------------------------------------------- */
.inspector-meter {
	background: #1a1a1a;
	border: 2px solid var(--mc-outline);
	box-sizing: content-box;
}

.inspector-meter__fill {
	background: var(--xp-green);
	box-shadow: inset 0 -2px 0 var(--xp-green-shade);
}

/* --- Scrollbar: slot track, raised panel thumb ------------------------- */
::-webkit-scrollbar {
	width: 14px;
	height: 14px;
}

::-webkit-scrollbar-track {
	background: var(--slot);
	box-shadow: var(--sunken-slot);
}

::-webkit-scrollbar-thumb {
	background: var(--panel);
	border: 2px solid var(--mc-outline);
	box-shadow: var(--raised);
}

::-webkit-scrollbar-corner {
	background: var(--panel);
}

/* Firefox has no ::-webkit-scrollbar; Chrome ignores those rules entirely
   once scrollbar-color is set, hence the @supports guard. */
@supports not selector(::-webkit-scrollbar) {
	* {
		scrollbar-color: var(--panel) var(--slot);
	}
}

/* --- Text straight on the dirt background ------------------------------ */
.inspector-intro__title,
.inspector-section__title,
.debug-section__title {
	font-family: var(--font-pixel);
	color: var(--ink-on-bg);
	text-shadow: 2px 2px 0 var(--text-shadow-color);
}

.inspector-intro__subtitle,
.debug-section__count,
.world-viewer-legend {
	color: var(--ink-on-bg-muted);
	text-shadow: 1px 1px 0 var(--text-shadow-color);
}

/* ====================================================================== */
/* End of Minecraft UI kit                                                */
/* ====================================================================== */

/* Every icon from icons.js carries this class. Sized in em by default so it
   tracks whatever dynamic/computed font-size surrounds it (badge text that
   scales with a building's footprint, etc.) without each call site having
   to compute a matching pixel size - callers that DO want a fixed pixel
   size still get one from the width/height attributes getIconSvg() sets,
   this only overrides for spots that opt in via a more specific selector. */
.mc-icon {
	flex-shrink: 0;
	vertical-align: -0.15em;
}

/* The grid is sized to comfortably cover a whole uploaded region file
   (512x512 blocks) or more, so it's normally bigger than the visible page -
   #root IS the scrollable viewport onto it (fixed to the space between the
   topbar/sidebars/bottom bar, native overflow:auto scrollbars/trackpad/
   wheel panning) rather than a flex-centered box that assumed the grid
   would always fit on screen. */
#root {
	position: fixed;
	top: var(--topbar-height);
	left: var(--left-sidebar-width);
	right: var(--preview-sidebar-width);
	bottom: var(--bottom-bar-height);
	overflow: auto;
	padding: 32px;
	transition:
		left 0.2s ease,
		right 0.2s ease;
}

body.left-pane-collapsed #root {
	left: var(--left-sidebar-collapsed-width);
}

body.preview-collapsed #root {
	right: var(--preview-sidebar-collapsed-width);
}

.app-nav {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	height: var(--topbar-height);
	display: flex;
	align-items: center;
	gap: 20px;
	padding: 0 20px;
	z-index: 30;
}

.app-nav__brand {
	display: flex;
	align-items: center;
	color: var(--ink);
	text-decoration: none;
	font-weight: 700;
	font-size: 16px;
	white-space: nowrap;
}

.app-nav__links {
	display: flex;
	gap: 4px;
	flex: 1;
	min-width: 0;
	/* min-width:0 alone only lets THIS flex item shrink below its content
	   size - it doesn't stop the nowrap links inside from rendering at
	   their full natural width once there's less room than they need
	   (narrow viewports, "Colony Inspector" being the long one), which
	   otherwise visually spills into the theme-toggle/shortcuts buttons
	   sitting right after this in .app-nav's own flex row. Scrolls within
	   its own slot instead, same pattern as the shape tray's horizontal
	   scroll below. */
	overflow-x: auto;
}

.app-nav__link {
	font-size: 12px;
	padding: 6px 14px;
	text-decoration: none;
	white-space: nowrap;
	flex-shrink: 0;
}

.app-nav__link.is-active {
	font-weight: 600;
}

.app-nav__theme-toggle,
.app-nav__shortcuts-toggle {
	font-size: 16px;
	line-height: 1;
	padding: 8px 10px;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
}

.app-nav__shortcuts-toggle {
	font-weight: 700;
}

.shortcuts-modal-overlay {
	position: fixed;
	inset: 0;
	background: rgba(8, 9, 12, 0.55);
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 50;
	padding: 24px;
}

.shortcuts-modal-overlay[hidden] {
	display: none;
}

.shortcuts-modal {
	width: min(560px, 100%);
	max-height: 80vh;
	overflow-y: auto;
	display: flex;
	flex-direction: column;
}

.shortcuts-modal__header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 16px 20px;
	position: sticky;
	top: 0;
	background: var(--panel);
	border-bottom: 2px solid var(--panel-lo);
}

.shortcuts-modal__header h2 {
	margin: 0;
	font-size: 20px;
}

.shortcuts-modal__close {
	font-size: 20px;
	line-height: 1;
	padding: 4px 9px;
	cursor: pointer;
}

.shortcuts-modal__body {
	padding: 16px 20px 20px;
	display: flex;
	flex-direction: column;
	gap: 18px;
}

.shortcuts-modal__group h3 {
	margin: 0 0 8px;
	font-size: 12px;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--ink-muted);
}

.shortcuts-modal__group dl {
	margin: 0;
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.shortcuts-modal__row {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 16px;
}

.shortcuts-modal__row dt {
	flex: 0 0 auto;
	white-space: nowrap;
}

.shortcuts-modal__row dd {
	margin: 0;
	text-align: right;
	color: var(--ink);
	font-size: 12px;
}

.shortcuts-modal kbd {
	font-size: 12px;
	font-family: var(--font-pixel);
	padding: 2px 6px;
}

@media (max-width: 560px) {
	.app-nav {
		gap: 10px;
		padding: 0 12px;
	}

	/* The brand is text only, so hiding it entirely rather than leaving an
	   empty, still-focusable link. */
	.app-nav__brand {
		display: none;
	}

	.app-nav__link {
		padding: 6px 10px;
	}
}

.left-sidebar {
	position: fixed;
	top: var(--topbar-height);
	left: 0;
	bottom: var(--bottom-bar-height);
	width: var(--left-sidebar-width);
	padding: 16px;
	overflow-y: auto;
	z-index: 8;
	transition:
		width 0.2s ease,
		padding 0.2s ease,
		height 0.2s ease;
}

.left-sidebar__header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	margin-bottom: 12px;
}

.left-sidebar__title {
	margin: 0;
	font-size: 16px;
	font-weight: 400;
}

.left-sidebar__toggle {
	font-size: 12px;
	padding: 6px 10px;
	cursor: pointer;
}

.left-sidebar__content {
	display: flex;
	flex-direction: column;
	/* Sections sit further apart (20px) than anything inside one (8px), so
	   each title clearly belongs to what's below it. */
	gap: 20px;
	padding: 12px;
	font-size: 12px;
	line-height: 16px;
	color: var(--ink-muted);
}

.left-sidebar__section {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.left-sidebar__section-title {
	margin: 0;
	font-size: 12px;
	line-height: 16px;
	font-weight: 700;
	text-transform: uppercase;
	color: var(--ink-muted);
}

.left-sidebar__button-row {
	display: flex;
	gap: 8px;
}

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

.plan-check__empty {
	margin: 0;
	font-size: 12px;
	color: var(--ink-muted);
}

.plan-check__group {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

/* A sub-heading under the "Plan Check" section title: regular weight and
   sentence case, so it doesn't read as another section. */
.plan-check__group-title {
	margin: 0;
	font-size: 12px;
	line-height: 16px;
	font-weight: 400;
	color: var(--ink);
}

.plan-check__item {
	display: flex;
	align-items: flex-start;
	gap: 8px;
	padding: 8px;
}

/* The button version: a full-width row, text left-aligned like the rest. */
.plan-check__item--fix {
	width: 100%;
	font-size: 12px;
	line-height: 16px;
	text-align: left;
	cursor: pointer;
}

/* The 15px icon centered on the title's first 16px line. */
.plan-check__icon {
	flex-shrink: 0;
	display: flex;
	height: 16px;
	align-items: center;
}

.plan-check__text {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
}

.plan-check__title {
	font-size: 12px;
	font-weight: 700;
	color: var(--ink);
}

.plan-check__detail {
	font-size: 12px;
	color: var(--ink-muted);
}

.left-sidebar__button-row .left-sidebar__button {
	flex: 1;
	text-align: center;
}

/* A short button beside a text field (Save As) stays on one line and lets
   the field take the rest of the row. */
.left-sidebar__button-row .left-sidebar__button--fit {
	flex: none;
	white-space: nowrap;
}

.left-sidebar__button {
	display: block;
	font-size: 12px;
	padding: 8px 12px;
	cursor: pointer;
	text-align: left;
	text-decoration: none;
	box-sizing: border-box;
}

.left-sidebar__button--danger {
	text-align: center;
}

.left-sidebar__text-input {
	flex: 1;
	font-size: 12px;
	padding: 8px 10px;
	min-width: 0;
}

.left-sidebar__text-input::placeholder {
	color: var(--ink-muted);
}

.left-sidebar__hint {
	margin: 0;
	font-size: 12px;
	color: var(--ink-muted);
}

/* An empty status line (World Background's, before any upload) takes no
   room, so it doesn't leave a double gap. */
.left-sidebar__hint:empty {
	display: none;
}

/* The description of a left-pane setting, shown on hover/focus
   (planner/sidebar-hints.js). */
.sidebar-tooltip {
	position: fixed;
	z-index: 60;
	max-width: 240px;
	padding: 6px 8px;
	font-size: 12px;
	line-height: 1.4;
	pointer-events: none;
}

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

.world-bg-controls {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.world-bg-controls__row {
	display: flex;
	/* A checkbox and its label stay together; a label that wraps keeps its
	   checkbox beside the first line. */
	justify-content: flex-start;
	align-items: flex-start;
	gap: 8px;
	font-size: 12px;
	color: var(--ink);
}

/* The 18px box centered on the label's first 16px line. */
.world-bg-controls__row input[type="checkbox"] {
	margin-top: -1px;
}

.left-sidebar__plans-list {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.left-sidebar__plans-empty {
	font-size: 12px;
	color: var(--ink-muted);
}

/* The name gets its own line (the pane is too narrow to fit it beside two
   buttons), with Load and Delete side by side under it. */
.left-sidebar__plan-row {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
	padding: 8px;
}

.left-sidebar__plan-name {
	flex: 1 0 100%;
	font-size: 12px;
	color: var(--ink);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	min-width: 0;
}

.left-sidebar__plan-action {
	flex: 1;
	font-size: 12px;
	padding: 4px 8px;
	cursor: pointer;
}

.left-sidebar.is-collapsed {
	width: var(--left-sidebar-collapsed-width);
	padding: 8px;
	overflow: hidden;
}

.left-sidebar.is-collapsed .left-sidebar__header {
	margin-bottom: 0;
	justify-content: center;
}

.left-sidebar.is-collapsed .left-sidebar__title,
.left-sidebar.is-collapsed .left-sidebar__content {
	display: none;
}

.left-sidebar.is-collapsed .left-sidebar__toggle {
	width: 100%;
	padding: 8px 0;
}

.preview-sidebar {
	position: fixed;
	top: var(--topbar-height);
	right: 0;
	bottom: var(--bottom-bar-height);
	width: var(--preview-sidebar-width);
	padding: 18px;
	overflow-y: auto;
	z-index: 8;
	transition:
		width 0.2s ease,
		padding 0.2s ease,
		height 0.2s ease;
}

.preview-sidebar__header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	margin-bottom: 12px;
}

.hover-coords {
	display: flex;
	align-items: baseline;
	gap: 8px;
	margin-bottom: 12px;
	padding: 8px 10px;
	font-size: 12px;
	font-variant-numeric: tabular-nums;
}

.hover-coords[hidden] {
	display: none;
}

.hover-coords__biome {
	color: var(--ink-muted);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.preview-sidebar__title {
	margin: 0;
	font-size: 16px;
	font-weight: 400;
}

.preview-sidebar__toggle {
	font-size: 12px;
	padding: 6px 10px;
	cursor: pointer;
}

.preview-card {
	display: flex;
	flex-direction: column;
	gap: 10px;
	padding: 12px;
}

.preview-card__name {
	font-size: 16px;
	font-weight: 400;
	color: var(--ink);
	word-break: break-word;
}

.preview-card__view-toggle {
	display: flex;
	/* Keeps the buttons at their natural height when the mobile layout
	   turns .preview-card into a row and stretches this toggle to full height. */
	align-items: flex-start;
	gap: 6px;
}

.preview-card__view-btn {
	font-size: 12px;
	padding: 4px 10px;
	cursor: pointer;
}

.preview-card__image-wrap {
	position: relative;
	aspect-ratio: 4 / 3;
	overflow: hidden;
	display: flex;
	align-items: center;
	justify-content: center;
}

.preview-card__image {
	max-width: 100%;
	max-height: 100%;
	object-fit: contain;
	display: none;
}

.preview-card__canvas {
	max-width: 100%;
	max-height: 100%;
	/* Same reasoning as World Viewer's canvas - a top-down building render is
	   made of crisp per-block icons, and smooth scaling would blur the exact
	   pixel edges that make fence/wall/stair shapes readable. */
	image-rendering: pixelated;
}

/* Category class (category-farming etc.) is set on this element in JS
   (showPreviewView) so --category-color/--category-border resolve the same
   way they do for a placed building's own badge - same tint, same source. */
.preview-card__topdown-label {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	width: max-content;
	max-width: calc(100% - 16px);
	padding: 5px 9px;
	font-size: 16px;
	font-weight: 700;
	line-height: 1.3;
	text-align: center;
	/* Same name tag as a top-down building on the grid (see
	   .placed-square.has-topdown .placed-badge) - fixed colors, not theme
	   tokens, since it sits over a render rather than over UI chrome. */
	background: rgba(0, 0, 0, 0.6);
	color: #ffffff;
	border: none;
	white-space: normal;
	overflow: visible;
	pointer-events: none;
}

body.hide-topdown-overlay .preview-card__topdown-label {
	display: none;
}

.preview-card__empty {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 12px;
	text-align: center;
	font-size: 12px;
	line-height: 1.4;
	color: var(--ink-muted);
}

.preview-sidebar.is-collapsed {
	width: var(--preview-sidebar-collapsed-width);
	padding: 8px;
	overflow: hidden;
}

.preview-sidebar.is-collapsed .preview-sidebar__header {
	margin-bottom: 0;
	justify-content: center;
}

.preview-sidebar.is-collapsed .preview-sidebar__title,
.preview-sidebar.is-collapsed .preview-card,
.preview-sidebar.is-collapsed .hover-coords {
	display: none;
}

.preview-sidebar.is-collapsed .preview-sidebar__toggle {
	width: 100%;
	padding: 8px 0;
}

.preview-sidebar.is-collapsed .cost-card {
	display: none;
}

.cost-card {
	display: flex;
	flex-direction: column;
	gap: 10px;
	margin-top: 12px;
	padding: 12px;
}

.cost-card[hidden] {
	display: none;
}

.cost-card__levels {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
}

.cost-card__level-button {
	font-size: 12px;
	padding: 5px 12px;
	cursor: pointer;
}

.cost-card__level-button.is-active {
	font-weight: 600;
}

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

.cost-card__section-title {
	margin: 0 0 6px;
	font-size: 12px;
	font-weight: 700;
	letter-spacing: 0.02em;
	text-transform: uppercase;
	color: var(--ink-muted);
}

.cost-card__materials {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 4px;
	max-height: 220px;
	overflow-y: auto;
}

.cost-card__material {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	font-size: 12px;
	padding: 4px 8px;
}

.cost-card__material-label {
	display: flex;
	align-items: center;
	gap: 6px;
	min-width: 0;
}

.cost-card__material-icon {
	width: 18px;
	height: 18px;
	flex-shrink: 0;
	image-rendering: pixelated;
}

/* Fences/walls/slabs/plates/buttons are textured identically to their parent
   block (a fence is literally UV-mapped with the plank texture), so the only
   way to hint at the actual shape is to crop the icon into a silhouette of
   it — still the real material color, just correctly shaped. */
.material-shape--fence {
	/* A front-on cross: one full-height post crossed by two full-width rails
	   — this is the material-list icon (glanced at in a list, next to a
	   name), so a recognizable elevation view reads better here than the
	   top-down post view used in the building renders, where "seen from
	   above" is the whole point. Disconnected rectangles need a mask (SVG),
	   not clip-path — a single polygon can't express unconnected regions. */
	-webkit-mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect x='37' y='0' width='26' height='100'/><rect x='0' y='13' width='100' height='20'/><rect x='0' y='57' width='100' height='20'/></svg>");
	mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect x='37' y='0' width='26' height='100'/><rect x='0' y='13' width='100' height='20'/><rect x='0' y='57' width='100' height='20'/></svg>");
	-webkit-mask-size: 100% 100%;
	mask-size: 100% 100%;
}

.material-shape--wall {
	-webkit-clip-path: inset(8% 34%);
	clip-path: inset(8% 34%);
}

.material-shape--stairs {
	/* Cost-panel icon only (see topdown_grid.js — top-down rendering keeps
	   stairs as a full block, since from directly above a staircase mostly
	   reads as a solid tread surface). A single step: an L-shaped silhouette
	   rising from bottom-left to top-right. */
	-webkit-clip-path: polygon(0 100%, 0 50%, 50% 50%, 50% 0, 100% 0, 100% 100%);
	clip-path: polygon(0 100%, 0 50%, 50% 50%, 50% 0, 100% 0, 100% 100%);
}

.material-shape--slab {
	-webkit-clip-path: inset(50% 0 0 0);
	clip-path: inset(50% 0 0 0);
}

.material-shape--plate {
	-webkit-clip-path: inset(72% 8% 6% 8%);
	clip-path: inset(72% 8% 6% 8%);
}

.material-shape--button {
	-webkit-clip-path: inset(34%);
	clip-path: inset(34%);
}

.cost-card__material-name {
	color: var(--ink);
	word-break: break-word;
}

.cost-card__material-count {
	color: var(--ink-muted);
	font-weight: 600;
	flex-shrink: 0;
}

.cost-card__empty {
	font-size: 12px;
	color: var(--ink-muted);
}

/* Sized in JS to the zoomed grid (planner/grid-view.js). This is what #root
   scrolls; .grid-shell inside it stays at natural size and is scaled with a
   transform, which doesn't affect scroll extent - hence this sizer. */
.grid-zoom-sizer {
	position: relative;
}

.grid-shell {
	position: absolute;
	top: 0;
	left: 0;
	transform-origin: 0 0;
}

.grid-zoom-controls {
	position: fixed;
	right: calc(var(--preview-sidebar-width) + 24px);
	bottom: calc(var(--bottom-bar-height) + 24px);
	display: flex;
	flex-direction: column;
	gap: 4px;
	z-index: 6;
	transition: right 0.2s ease;
}

body.preview-collapsed .grid-zoom-controls {
	right: calc(var(--preview-sidebar-collapsed-width) + 24px);
}

/* One fixed compass for the whole map (top-right, same collapse-aware
   right offset as .grid-zoom-controls) - replaces the old per-building
   compass badge that used to render on every top-down building tile. */
.map-compass {
	position: fixed;
	/* Below .app-nav (fixed, top:0, z-index:30) - anchoring straight to the
	   viewport top would tuck this under the nav bar instead of floating
	   over the grid like .grid-zoom-controls does at the bottom. */
	top: calc(var(--topbar-height) + 16px);
	right: calc(var(--preview-sidebar-width) + 24px);
	z-index: 6;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 1px;
	width: 34px;
	height: 34px;
	pointer-events: none;
}

body.preview-collapsed .map-compass {
	right: calc(var(--preview-sidebar-collapsed-width) + 24px);
}

.map-compass__arrow {
	font-size: 12px;
	line-height: 1;
}

.map-compass__label {
	font-size: 12px;
	font-weight: 700;
	line-height: 1;
}

.grid-zoom-controls button {
	width: 32px;
	height: 32px;
	font-size: 16px;
	line-height: 1;
	cursor: pointer;
}

.grid-goto-controls {
	display: flex;
	align-items: flex-end;
	gap: 6px;
	margin-bottom: 12px;
	padding: 8px;
}

.preview-sidebar.is-collapsed .grid-goto-controls {
	display: none;
}

.grid-goto-controls label {
	display: flex;
	flex-direction: column;
	gap: 2px;
	font-size: 12px;
	color: var(--ink-muted);
}

.grid-goto-controls input[type="number"] {
	font-size: 12px;
	padding: 5px 6px;
	width: 72px;
	box-sizing: border-box;
}

.grid-goto-controls button {
	height: 30px;
	padding: 0 12px;
	font-size: 12px;
	cursor: pointer;
}

/* Gridlines are a pure CSS background pattern, not one DOM element per cell
   (the old .grid-cell approach) - at the grid sizes needed to make "scroll
   around freely" actually useful (covering a full uploaded region file,
   512x512+ blocks), one div per cell would mean hundreds of thousands of DOM
   nodes just to draw lines. Two gradient pairs stacked in one background:
   the fine per-cell lines tile at --cell-size, the bolder chunk-boundary
   lines tile at --chunk-size on top of them (first-listed background layer
   paints on top in CSS). */
.grid {
	position: relative;
	background:
		linear-gradient(to right, var(--map-grid-major) 2px, transparent 2px) 0 0 /
			var(--chunk-size) var(--chunk-size),
		linear-gradient(to bottom, var(--map-grid-major) 2px, transparent 2px) 0 0 /
			var(--chunk-size) var(--chunk-size),
		linear-gradient(to right, var(--map-grid-minor) 1px, transparent 1px) 0 0 /
			var(--cell-size) var(--cell-size),
		linear-gradient(to bottom, var(--map-grid-minor) 1px, transparent 1px) 0 0 /
			var(--cell-size) var(--cell-size),
		var(--grid-bg);
	border: 2px solid var(--mc-outline);
	cursor: grab;
	/* Without these, the browser's own touch and text-selection handling
	   can swallow a trackpad drag before the grid's gestures see it. */
	touch-action: none;
	user-select: none;
	-webkit-user-select: none;
}

/* The "Show grid lines" setting - drops both line layers, leaving just the
   flat base fill. */
.grid.hide-grid-lines {
	background: var(--grid-bg);
}

.grid.is-panning {
	cursor: grabbing;
}

/* With a world background (planner/world-background.js) behind the grid,
   only the grid's fill turns transparent - its line layers stay - so the
   map shows between the lines. */
.grid.has-world-bg {
	background-color: transparent;
}

.world-bg-layer {
	position: absolute;
	top: 0;
	left: 0;
	overflow: hidden;
}

.world-bg-layer canvas {
	position: absolute;
	image-rendering: pixelated;
	display: block;
}

.bottom-bar {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	padding: 14px 24px 8px;
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: 12px;
	color: var(--ink);
}

.bottom-top {
	display: flex;
	align-items: flex-start;
	gap: 12px;
	overflow-x: auto;
	overflow-y: hidden;
}

.category-stack {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.bottom-bottom {
	display: flex;
	height: var(--bottom-tray-height);
	overflow-x: auto;
	overflow-y: hidden;
	padding-bottom: 0;
	box-sizing: border-box;
}

.tab-bar {
	display: flex;
	align-items: center;
	gap: 6px;
	flex-wrap: nowrap;
	min-width: max-content;
}

.subtab-bar {
	display: flex;
	align-items: center;
	gap: 6px;
	flex-wrap: nowrap;
	min-width: max-content;
	min-height: 28px;
}

.subtab-bar.is-hidden {
	visibility: hidden;
}

.subtab {
	font-size: 12px;
	padding: 5px 12px;
	cursor: pointer;
}

.subtab.is-active {
	font-weight: 600;
}

.tab {
	font-size: 12px;
	padding: 7px 16px;
	cursor: pointer;
}

.tab.is-active {
	font-weight: 600;
}

/* Same fixed height as .shape-search-input next to it, so the two line up. */
.style-select {
	height: 32px;
	font-size: 12px;
	padding: 0 10px;
}

.shape-search-input {
	height: 32px;
	font-size: 12px;
	padding: 0 10px;
	width: 170px;
}

/* Lives inside the shape tray (see renderShapeTray's "roads" branch), not
   the always-visible bottom-top bar - a column layout (hint above the
   controls row) suits that flex-row tray slot better than the inline strip
   this used when it sat next to the style selector. */
.path-controls {
	display: flex;
	flex-direction: column;
	gap: 10px;
	padding: 8px 4px;
}

.path-controls__hint {
	margin: 0;
	font-size: 12px;
	color: var(--ink-muted);
}

.path-type-picker {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
}

.path-type-swatch {
	display: flex;
	align-items: center;
	gap: 6px;
	font-size: 12px;
	padding: 5px 10px 5px 6px;
	cursor: pointer;
}

.path-type-swatch.is-active {
	font-weight: 600;
}

.path-type-swatch__color {
	width: 14px;
	height: 14px;
	border: 1px solid rgba(12, 12, 18, 0.55);
	flex-shrink: 0;
}

/* "walls" drags out real schematic buildings on release instead of
   painting a flat color like every other type in this row (see
   renderPathTypeOptions) - an extra-wide gap separates it from the paint
   colors, and its swatch is drawn as a bevelled block (vs. every other
   type's flat paint chip) to hint "this is a building piece", not a color
   choice, before the click. */
.path-type-swatch--building {
	margin-left: 12px;
}

.path-type-swatch--building .path-type-swatch__color {
	box-shadow:
		inset 2px 2px 0 rgba(255, 255, 255, 0.45),
		inset -2px -2px 0 rgba(0, 0, 0, 0.4);
}

.path-controls__row {
	display: flex;
	align-items: center;
	gap: 8px;
}

/* Path's width stepper, or canal's fixed width as text. */
.path-controls__field {
	display: flex;
	align-items: center;
	gap: 6px;
	font-size: 12px;
	color: var(--ink-muted);
	white-space: nowrap;
}

/* A number field between stone − / + buttons, the same height as the
   eraser button beside it. The browser's own spinner arrows are hidden -
   they can't be styled to match. */
.number-stepper {
	display: inline-flex;
	align-items: stretch;
	gap: 2px;
}

.number-stepper[hidden] {
	display: none;
}

.number-stepper__button {
	width: 30px;
	height: 30px;
	padding: 0;
	font-size: 16px;
	line-height: 1;
	cursor: pointer;
}

.number-stepper__input {
	box-sizing: border-box;
	width: 40px;
	height: 30px;
	padding: 0 4px;
	font-size: 16px;
	text-align: center;
	appearance: textfield;
	-moz-appearance: textfield;
}

.number-stepper__input::-webkit-inner-spin-button,
.number-stepper__input::-webkit-outer-spin-button {
	-webkit-appearance: none;
	margin: 0;
}

.path-controls__erase-toggle {
	font-size: 12px;
	padding: 6px 14px;
	cursor: pointer;
	height: 30px;
}

.shape-tray {
	display: flex;
	flex-direction: row;
	flex-wrap: nowrap;
	gap: 12px;
	align-items: flex-start;
	min-width: max-content;
}

/* Fixed-size card: an 88px square slot, then a two-line name, then the
   footprint. Every card is identical in size whatever the building. The
   6px padding covers the well's 2px edge plus the 4px selection frame
   drawn outside the slot (.is-selected), so the frame never overlaps it. */
.shape-option {
	/* 96px of text width - the widest single-word names (Swineherder) fit
	   without being broken mid-word. */
	width: 108px;
	padding: 6px;
	cursor: pointer;
	display: grid;
	justify-items: center;
	gap: 4px;
	align-content: start;
	text-align: center;
}

/* Shows the photo when there is one, otherwise the flat category-colored
   name badge (see loadShapeTrayThumbnail). */
.shape-preview {
	position: relative;
	width: 88px;
	height: 88px;
}

.shape-preview__image {
	position: absolute;
	inset: 4px;
	width: calc(100% - 8px);
	height: calc(100% - 8px);
	object-fit: contain;
}

.preview-badge {
	position: absolute;
	inset: 4px;
	display: flex;
	align-items: center;
	justify-content: center;
	text-align: center;
	font-size: 12px;
	line-height: 1.2;
	padding: 4px;
	background: color-mix(
		in srgb,
		var(--category-color, rgba(255, 255, 255, 0.2)) 25%,
		rgba(255, 255, 255, 0.72)
	);
	border: 1px solid rgba(12, 12, 18, 0.55);
	color: var(--badge-ink);
	font-weight: 700;
}

/* .preview-badge's own display:flex above is an author-stylesheet rule, so
   it beats the [hidden] attribute's UA-stylesheet display:none by origin
   regardless of selector specificity - this class+attribute selector is
   both more specific AND still an author rule, so it wins instead. Set via
   badge.hidden in loadShapeTrayThumbnail once a shape tray card's real
   preview photo loads (the photo replaces the flat-color+badge fallback,
   not layers under it - the badge would otherwise still visibly cover the
   photo since both are position:absolute; inset:0). */
.preview-badge[hidden] {
	display: none;
}

/* Room for exactly two lines on every card, so cards stay the same height
   whether a name wraps or not; longer names are cut with an ellipsis (the
   full name is in the button's title tooltip). */
.shape-label {
	width: 100%;
	font-size: 12px;
	line-height: 14px;
	min-height: 28px;
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	overflow: hidden;
	overflow-wrap: break-word;
}

.shape-dimensions {
	font-size: 12px;
}

.placed-square {
	background-color: var(--category-color, #d8c4a8);
	background-image:
		linear-gradient(
			to right,
			var(--category-line, rgba(90, 70, 50, 0.35)) 1px,
			transparent 1px
		),
		linear-gradient(
			to bottom,
			var(--category-line, rgba(90, 70, 50, 0.35)) 1px,
			transparent 1px
		);
	background-size: var(--cell-size) var(--cell-size);
	/* content-box (overriding the page-wide border-box reset) so this
	   border adds OUTWARD from the tile's own footprint instead of eating
	   into it - two abutting placed-squares (e.g. wall segments tiled
	   edge-to-edge) then just overlap borders by a pixel or two at the
	   shared seam rather than each shrinking its own fill and stacking two
	   full-width borders into a gap-looking band between them. */
	box-sizing: content-box;
	border: 1px solid var(--category-border, #b59a7a);
}

.placed-square {
	position: absolute;
	cursor: grab;
	/* Above .placed-path (z-index: 1 below) regardless of DOM order - paths
	   get removed/re-appended to .grid on every edit (paint, drag, undo),
	   so relying on insertion order alone would make a road painted or
	   moved after a building was placed render on top of it. */
	z-index: 2;
}

.placed-path {
	z-index: 1;
}

/* Every cell of a painted path is drawn into this one canvas (see
   planner/paths.js) - pixelated so zooming in keeps cell edges crisp. */
.placed-path__canvas {
	display: block;
	width: 100%;
	height: 100%;
	image-rendering: pixelated;
	pointer-events: none;
}

.placed-badge {
	position: absolute;
	inset: 0;
	/* Above the rooftop canvas and door markers, which come after it in
	   the DOM - at equal z-index they would hide the top-down name tag. */
	z-index: 2;
	display: flex;
	align-items: center;
	justify-content: center;
	text-align: center;
	font-size: 12px;
	line-height: 1.2;
	padding: 4px;
	background: color-mix(
		in srgb,
		var(--category-color, rgba(255, 255, 255, 0.2)) 25%,
		rgba(255, 255, 255, 0.72)
	);
	border: 1px solid rgba(12, 12, 18, 0.55);
	user-select: none;
	-webkit-user-select: none;
	color: var(--badge-ink);
	font-weight: 700;
}

/* Wall pieces tile into long thin runs, where a label on every piece is
   clutter, so walls have no badge in either mode. The .has-topdown variant
   out-specifies the later `.placed-square.has-topdown .placed-badge` rule,
   which would otherwise show it again. */
.placed-square.category-walls .placed-badge,
.placed-square.category-walls.has-topdown .placed-badge {
	display: none;
}

/* Exterior doors on a top-down render (planner/buildings.js): a bar across
   each doorway in Minecraft's light purple, outlined dark so it reads on any
   roof. Sized in screen pixels (divided by the zoom), never smaller than its
   block, so it doesn't shrink away when zoomed out. */
.placed-square__doors {
	position: absolute;
	inset: 0;
	pointer-events: none;
	z-index: 1;
}

.door-marker {
	--door-outline: calc(1.5px / var(--grid-zoom, 1));
	position: absolute;
	transform: translate(-50%, -50%);
	background: #ff55ff;
	box-shadow: 0 0 0 var(--door-outline) #200020;
}

.door-marker--horizontal {
	width: max(5px, calc(14px / var(--grid-zoom, 1)));
	height: max(2px, calc(5px / var(--grid-zoom, 1)));
}

.door-marker--vertical {
	width: max(2px, calc(5px / var(--grid-zoom, 1)));
	height: max(5px, calc(14px / var(--grid-zoom, 1)));
}

.placed-square__rooftop-canvas {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	image-rendering: pixelated;
	pointer-events: none;
	z-index: 0;
}

/* A top-down-rendered building shrinks its badge down to a small name tag
   floating in the middle of the square instead of the flat mode's
   full-square label, so the render underneath stays legible. Styled as a
   Minecraft mob name tag: translucent black box, white text, in fixed
   colors regardless of theme (it sits over a render, not UI chrome; the
   game's own tags are ~25% black - darker here so it holds up over busy
   rooftops). Text wraps rather than truncating. The tag itself (not just its text) sits centered in the middle of the
   square - width hugs its content up to the square's own size, via the
   translate(-50%,-50%) centering trick. */
.placed-square.has-topdown .placed-badge {
	inset: auto;
	top: 50%;
	left: 50%;
	right: auto;
	bottom: auto;
	transform: translate(-50%, -50%);
	width: max-content;
	max-width: calc(100% - 8px);
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 4px 7px;
	font-size: 12px;
	line-height: 1.25;
	background: rgba(0, 0, 0, 0.6);
	color: #ffffff;
	border: none;
	text-align: center;
	white-space: normal;
	overflow: visible;
}

body.hide-topdown-overlay .placed-square.has-topdown .placed-badge {
	display: none;
}

body.hide-topdown-overlay .placed-square.has-topdown {
	background-color: transparent;
	background-image: none;
	border-color: transparent;
	box-shadow: none;
}

.category-farming {
	--category-color: rgba(166, 218, 149, 0.35);
	--category-line: rgba(166, 218, 149, 0.6);
	--category-border: rgba(166, 218, 149, 0.9);
}

.category-craftsmanship {
	--category-color: rgba(245, 169, 127, 0.35);
	--category-line: rgba(245, 169, 127, 0.6);
	--category-border: rgba(245, 169, 127, 0.9);
}

.category-decoration {
	--category-color: rgba(139, 213, 202, 0.35);
	--category-line: rgba(139, 213, 202, 0.6);
	--category-border: rgba(139, 213, 202, 0.9);
}

.category-education {
	--category-color: rgba(125, 196, 228, 0.35);
	--category-line: rgba(125, 196, 228, 0.6);
	--category-border: rgba(125, 196, 228, 0.9);
}

.category-fundamentals {
	--category-color: rgba(238, 212, 159, 0.35);
	--category-line: rgba(238, 212, 159, 0.6);
	--category-border: rgba(238, 212, 159, 0.9);
}

.category-infrastructure {
	--category-color: rgba(148, 156, 182, 0.35);
	--category-line: rgba(148, 156, 182, 0.6);
	--category-border: rgba(148, 156, 182, 0.9);
}

.category-military {
	--category-color: rgba(237, 135, 150, 0.35);
	--category-line: rgba(237, 135, 150, 0.6);
	--category-border: rgba(237, 135, 150, 0.9);
}

.category-mystic {
	--category-color: rgba(198, 160, 246, 0.35);
	--category-line: rgba(198, 160, 246, 0.6);
	--category-border: rgba(198, 160, 246, 0.9);
}

.category-walls {
	--category-color: rgba(128, 135, 162, 0.35);
	--category-line: rgba(128, 135, 162, 0.6);
	--category-border: rgba(128, 135, 162, 0.9);
}

.placed-square.is-selected {
	outline: 3px solid var(--accent-selected);
	outline-offset: 1px;
}

.placed-square.is-dragging {
	cursor: grabbing;
	opacity: 0.85;
	/* Over the buildings it's dragged across. */
	z-index: 3;
}

/* Dragged somewhere it can't stay (over another building): red like a
   blocked placement ghost, outline and tint. Dropped there, it snaps back. */
.placed-square.is-blocked {
	outline-color: var(--ghost-blocked);
}

.placed-square.is-blocked::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 3;
	background: color-mix(in srgb, var(--ghost-blocked) 40%, transparent);
	pointer-events: none;
}

.action-menu {
	position: absolute;
	z-index: 5;
	display: none;
	flex-direction: column;
	gap: 6px;
	padding: 8px;
}

.action-menu button {
	font-size: 12px;
	padding: 6px 10px;
	cursor: pointer;
	text-align: left;
}

.path-action-menu {
	position: absolute;
	z-index: 5;
	display: none;
	flex-direction: column;
	gap: 6px;
	padding: 8px;
}

.path-action-menu button {
	font-size: 12px;
	padding: 6px 10px;
	cursor: pointer;
	text-align: left;
}

.placed-path.is-selected {
	outline: 2px solid var(--path-selected-outline);
	outline-offset: -1px;
	box-shadow: inset 0 0 3px rgba(255, 255, 255, 0.3);
}

.eraser-hover-preview {
	position: absolute;
	z-index: 5;
	display: none;
	box-sizing: border-box;
	background: rgba(224, 86, 86, 0.22);
	border: 2px dashed #e05656;
	pointer-events: none;
}

.marquee-select {
	position: absolute;
	z-index: 5;
	display: none;
	box-sizing: border-box;
	background: color-mix(in srgb, var(--accent-selected) 18%, transparent);
	border: 1px solid var(--accent-selected);
	pointer-events: none;
}

/* The placement ghost (planner/placement-ghost.js): the armed building where
   a click would put it, outlined green if it fits and red if it doesn't. */
.placement-ghosts {
	position: absolute;
	inset: 0;
	z-index: 4;
	pointer-events: none;
}

.placement-ghosts[hidden],
.placement-ghost[hidden] {
	display: none;
}

.placement-ghost {
	position: absolute;
	box-sizing: border-box;
	display: flex;
	align-items: center;
	justify-content: center;
	background-color: var(--category-color, rgba(216, 196, 168, 0.5));
	outline: 2px solid var(--ghost-fits);
	outline-offset: -2px;
}

/* A tint over the whole ghost, render included. */
.placement-ghost::after {
	content: "";
	position: absolute;
	inset: 0;
	background: color-mix(in srgb, var(--ghost-fits) 20%, transparent);
}

.placement-ghosts.is-blocked .placement-ghost {
	outline-color: var(--ghost-blocked);
}

.placement-ghosts.is-blocked .placement-ghost::after {
	background: color-mix(in srgb, var(--ghost-blocked) 30%, transparent);
}

/* A click where it doesn't fit flashes it a stronger red. */
.placement-ghosts.is-denied .placement-ghost::after {
	background: color-mix(in srgb, var(--ghost-blocked) 55%, transparent);
}

.placement-ghost__render {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	opacity: 0.8;
	image-rendering: pixelated;
}

.placement-ghost__render[hidden] {
	display: none;
}

/* The name, as a mob-style name tag; hidden over a render, and on walls
   (which tile into runs), as on placed buildings. */
.placement-ghost__label {
	position: relative;
	z-index: 1;
	padding: 2px 4px;
	font-family: var(--font-pixel);
	font-size: 12px;
	line-height: 16px;
	text-align: center;
	color: #fff;
	background: rgba(0, 0, 0, 0.55);
}

.placement-ghost.has-render .placement-ghost__label,
.placement-ghost.category-walls .placement-ghost__label {
	display: none;
}

.placement-ghosts__message {
	position: absolute;
	z-index: 2;
	display: none;
	padding: 4px 8px;
	font-size: 12px;
	line-height: 16px;
	white-space: nowrap;
	transform: translateY(calc(-100% - 6px));
}

.placement-ghosts.is-denied .placement-ghosts__message {
	display: block;
}

.run-draw-preview {
	position: absolute;
	z-index: 5;
	display: none;
	box-sizing: border-box;
	background: rgba(150, 150, 156, 0.35);
	border: 2px dashed rgba(150, 150, 156, 0.9);
	pointer-events: none;
}

@media (max-width: 1024px) {
	/* Redefines (not self-references - a literal px restated, not var()) the
	   three fixed mobile panel/tray heights from :root above with a
	   vh-capped version, picked up automatically by every consumer of these
	   same custom properties (.left-sidebar/.preview-sidebar/.bottom-bottom's
	   own height rules AND #root's padding-top/-bottom calc) with no changes
	   needed anywhere else. On a short viewport (a landscape phone, or just
	   a browser window resized short) these fixed panels used to claim
	   their full 120px/188px/170px regardless of how little height was left
	   over for the grid - the actual point of the app - after the topbar
	   and the rest of the bottom bar were subtracted too. Verified against
	   an iPhone-SE-sized 375x667 viewport, where the uncapped total
	   (topbar + left strip + bottom bar + preview strip, padding included)
	   added up to MORE than the viewport's own height - #root's box was
	   being forced taller than the screen just to fit its own padding,
	   leaving zero visible room for the grid.
	   :root, not this block's own selector: a plain :root here still only
	   applies while this media query matches (media-scoped custom property
	   overrides work in the cascade like anything else, and the surrounding
	   @media already gates it), and using :root keeps the specificity/
	   selector identical to where these were first defined. */
	:root {
		--left-sidebar-mobile-height: min(120px, 16vh);
		--preview-sidebar-mobile-height: min(188px, 22vh);
		--bottom-tray-height: min(170px, 14vh);
	}

	#root {
		/* The base (desktop) rule insets #root by the sidebars' full widths
		   (--left-sidebar-width: 240px, --preview-sidebar-width: 320px) to
		   sit between two side-by-side columns - below this breakpoint the
		   sidebars reposition to horizontal bands (left:0; right:0; see
		   .left-sidebar/.preview-sidebar below) stacked above/below the grid
		   instead, so #root needs to span the full width too (inset only by
		   its own small padding) rather than keeping the desktop columns'
		   240px/320px reservations - left uncorrected, that leaves a content
		   area of `100vw - 240px - 320px`, which goes negative below a
		   560px-wide viewport and was squeezing the grid down to nothing.
		   left/right, not the whole position shorthand: top/bottom still
		   come from the base rule's var(--topbar-height)/var(--bottom-bar-
		   height), unchanged - only the horizontal insets are wrong here. */
		left: 0;
		right: 0;
		padding-left: 20px;
		padding-right: 20px;
		padding-top: calc(
			var(--topbar-height) + var(--left-sidebar-mobile-height) + 20px
		);
		padding-bottom: calc(
			var(--bottom-bar-height) + var(--preview-sidebar-mobile-height) + 24px
		);
	}

	/* Same specificity as the base (desktop) body.left-pane-collapsed #root
	   rule that sets left: var(--left-sidebar-collapsed-width) (240px-scale
	   column collapse) - without repeating left:0 here too, that desktop
	   rule would win over this media query's own plain #root{left:0} above
	   purely on selector specificity, regardless of source order. */
	body.left-pane-collapsed #root {
		left: 0;
		padding-top: calc(
			var(--topbar-height) + var(--left-sidebar-mobile-collapsed-height) + 20px
		);
	}

	body.preview-collapsed #root {
		right: 0;
		padding-bottom: calc(
			var(--bottom-bar-height) +
				var(--preview-sidebar-mobile-collapsed-height) + 24px
		);
	}

	.left-sidebar {
		left: 0;
		right: 0;
		top: var(--topbar-height);
		bottom: auto;
		width: auto;
		height: var(--left-sidebar-mobile-height);
	}

	.left-sidebar.is-collapsed {
		left: 0;
		right: 0;
		width: auto;
		height: var(--left-sidebar-mobile-collapsed-height);
		padding: 8px 12px;
	}

	.left-sidebar__title {
		font-size: 16px;
	}

	.left-sidebar__header {
		margin-bottom: 8px;
	}

	.left-sidebar.is-collapsed .left-sidebar__header {
		margin-bottom: 0;
		justify-content: flex-start;
	}

	.left-sidebar.is-collapsed .left-sidebar__toggle {
		width: auto;
		padding: 6px 10px;
	}

	.preview-sidebar {
		left: 0;
		right: 0;
		top: auto;
		bottom: var(--bottom-bar-height);
		width: auto;
		height: var(--preview-sidebar-mobile-height);
	}

	.preview-sidebar.is-collapsed {
		left: 0;
		right: 0;
		width: auto;
		height: var(--preview-sidebar-mobile-collapsed-height);
		padding: 8px 12px;
	}

	.preview-sidebar__title {
		font-size: 16px;
	}

	.preview-sidebar__header {
		margin-bottom: 8px;
	}

	.preview-card {
		flex-direction: row;
		align-items: stretch;
		height: calc(100% - 22px);
	}

	.preview-sidebar.is-collapsed .preview-sidebar__header {
		margin-bottom: 0;
		justify-content: flex-end;
	}

	.preview-sidebar.is-collapsed .preview-sidebar__toggle {
		width: auto;
		padding: 6px 10px;
	}

	.preview-card__name {
		width: 130px;
		font-size: 12px;
	}

	.preview-card__image-wrap {
		flex: 1;
		aspect-ratio: auto;
	}
}

/* ---------------------------------------------------------------------- */
/* Colony Inspector page                                                  */
/* ---------------------------------------------------------------------- */

#inspector-root,
#world-viewer-root {
	max-width: 1100px;
	margin: 0 auto;
	padding: calc(var(--topbar-height) + 32px) 24px 64px;
	display: flex;
	flex-direction: column;
	gap: 24px;
}

#debug-root {
	/* Wider than the other utility pages - a dense icon/color review grid
	   benefits from more columns, unlike the single-focus upload tools. */
	max-width: 1400px;
	margin: 0 auto;
	padding: calc(var(--topbar-height) + 32px) 24px 64px;
	display: flex;
	flex-direction: column;
	gap: 24px;
}

.inspector-intro__title {
	margin: 0;
	font-size: 24px;
}

.inspector-intro__subtitle {
	margin: 4px 0 0;
	font-size: 16px;
}

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

.inspector-upload__zone {
	padding: 32px 24px;
	text-align: center;
	transition:
		border-color 0.15s ease,
		background 0.15s ease;
}

.inspector-upload__prompt {
	margin: 0 0 12px;
	color: var(--ink-muted);
}

.inspector-upload__prompt code {
	color: var(--ink);
}

.inspector-upload__button {
	font-size: 16px;
	padding: 10px 18px;
	cursor: pointer;
}

.inspector-upload__status {
	margin: 12px 0 0;
	font-size: 12px;
	color: var(--ink-muted);
	min-height: 1.2em;
}

.inspector-upload__link-button {
	display: inline-block;
	margin-top: 4px;
	font: inherit;
	font-size: 12px;
	color: var(--link);
	background: none;
	border: none;
	cursor: pointer;
	text-decoration: underline;
	padding: 0;
}

.inspector-upload__link-button:hover {
	filter: brightness(1.1);
}

.inspector-help {
	padding: 10px 14px;
}

.inspector-help summary {
	cursor: pointer;
	font-size: 12px;
	color: var(--ink);
}

.inspector-help__body {
	margin-top: 10px;
	display: flex;
	flex-direction: column;
	gap: 8px;
	font-size: 12px;
	color: var(--ink-muted);
}

.inspector-help__body code {
	color: var(--ink);
}

.inspector-notice {
	padding: 12px 14px;
	font-size: 12px;
	color: var(--ink-muted);
}

.inspector-picker {
	display: flex;
}

.inspector-picker__label {
	display: flex;
	flex-direction: column;
	gap: 6px;
	font-size: 12px;
	color: var(--ink-muted);
}

.inspector-picker__select {
	font-size: 16px;
	padding: 8px 10px;
}

.inspector-card {
	padding: 20px;
}

.inspector-card__title {
	margin: 0 0 16px;
	font-size: 20px;
}

.inspector-stat-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
	gap: 12px;
}

.inspector-stat-tile {
	display: flex;
	flex-direction: column;
	gap: 4px;
	padding: 10px 12px;
}

.inspector-stat-tile__label {
	font-size: 12px;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--ink-muted);
}

/* Regular weight: the game font's bold is too wide for short values like
   "caledonia" to fit a tile without breaking mid-word. Only genuinely long
   ids (minecraft:overworld) still wrap. */
.inspector-stat-tile__value {
	font-size: 16px;
	font-weight: 400;
	overflow-wrap: anywhere;
}

.inspector-stat-tile__value--swatch {
	display: flex;
	align-items: center;
	gap: 6px;
	font-weight: 400;
	font-size: 12px;
}

.inspector-swatch {
	width: 14px;
	height: 14px;
	border: 2px solid var(--panel-lo);
	display: inline-block;
}

.inspector-chip-row {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin-top: 14px;
}

.inspector-chip {
	font-size: 12px;
	padding: 4px 10px;
}

.inspector-chip--small {
	font-size: 12px;
	padding: 3px 8px;
}

.inspector-section {
	display: flex;
	flex-direction: column;
	gap: 14px;
}

.inspector-section__title {
	margin: 0;
	font-size: 20px;
}

.inspector-citizen-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
	gap: 14px;
}

.inspector-citizen-card {
	padding: 14px;
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.inspector-citizen-card__header {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 8px;
}

.inspector-citizen-card__name {
	margin: 0;
	font-size: 16px;
}

.inspector-citizen-card__gender {
	display: flex;
	align-items: center;
	color: var(--ink-muted);
}

.inspector-citizen-card__meta {
	display: flex;
	justify-content: space-between;
	font-size: 12px;
	color: var(--ink-muted);
}

.inspector-meter-row {
	display: grid;
	/* Wide enough for "Saturation" in the game font. */
	grid-template-columns: 84px 1fr auto;
	align-items: center;
	gap: 8px;
	font-size: 12px;
}

.inspector-meter-row__label {
	color: var(--ink-muted);
}

.inspector-meter-row__value {
	color: var(--ink-muted);
	white-space: nowrap;
}

.inspector-meter {
	display: block;
	height: 6px;
	overflow: hidden;
}

.inspector-meter__fill {
	display: block;
	height: 100%;
}

.inspector-citizen-card__skills {
	display: flex;
	flex-wrap: wrap;
	gap: 4px 8px;
	font-size: 12px;
	color: var(--ink-muted);
}

.inspector-citizen-card__buildings {
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
}

.inspector-citizen-card__happiness-detail summary {
	cursor: pointer;
	font-size: 12px;
	color: var(--ink-muted);
}

.inspector-table {
	width: 100%;
	border-collapse: collapse;
	font-size: 12px;
}

.inspector-table th,
.inspector-table td {
	text-align: left;
	padding: 8px 10px;
	border-bottom: 2px solid var(--panel-lo);
}

.inspector-table th {
	color: var(--ink-muted);
	font-weight: 600;
	font-size: 12px;
}

.inspector-raw {
	padding: 10px 14px;
}

.inspector-raw summary {
	cursor: pointer;
	font-size: 12px;
}

.inspector-raw__list {
	list-style: none;
	margin: 6px 0 0;
	padding-left: 16px;
	border-left: 2px solid var(--panel-lo);
	font-family: var(--font-pixel);
	font-size: 12px;
}

.inspector-raw__key {
	color: var(--ink-muted);
}

.inspector-raw__leaf {
	color: var(--ink);
}

.inspector-raw__truncated {
	color: var(--ink-muted);
	font-style: italic;
}

.world-viewer-upload-buttons {
	display: flex;
	justify-content: center;
	gap: 10px;
}

.world-viewer-results {
	max-width: 900px;
	margin: 20px auto 0;
	display: flex;
	flex-direction: column;
	gap: 14px;
}

.world-viewer-results[hidden] {
	display: none;
}

.world-viewer-canvas-wrap {
	position: relative;
	height: 560px;
	overflow: hidden;
	cursor: grab;
	touch-action: none;
	user-select: none;
}

.world-viewer-canvas-wrap.is-panning {
	cursor: grabbing;
}

.world-viewer-canvas-inner {
	position: absolute;
	top: 0;
	left: 0;
	transform-origin: 0 0;
}

.world-viewer-canvas-wrap canvas {
	image-rendering: pixelated;
	display: block;
}

/* Below 1:1 zoom the canvas is being shrunk, not enlarged - "pixelated"
   nearest-neighbor sampling skips most source pixels when shrinking, which
   erases 1px-wide contour lines almost entirely (confirmed: a multi-region
   map at 0.65x zoom rendered with no visible contour/hillshade texture at
   all, just flat biome color). Smooth interpolation properly area-averages
   instead, so those thin lines still contribute a faint darkening rather
   than vanishing. Toggled by JS based on the current zoom level. */
.world-viewer-canvas-wrap canvas.is-downscaled {
	image-rendering: auto;
}

.world-viewer-goto {
	position: absolute;
	left: 12px;
	top: 12px;
	display: flex;
	align-items: flex-end;
	gap: 6px;
	padding: 8px;
	z-index: 4;
}

.world-viewer-goto label {
	display: flex;
	flex-direction: column;
	gap: 2px;
	font-size: 12px;
	color: var(--ink-muted);
}

.world-viewer-goto input[type="number"] {
	font-size: 12px;
	padding: 5px 6px;
	width: 72px;
	box-sizing: border-box;
}

.world-viewer-goto button {
	height: 30px;
	padding: 0 12px;
	font-size: 12px;
	cursor: pointer;
}

.world-viewer-zoom-controls {
	position: absolute;
	right: 12px;
	bottom: 12px;
	display: flex;
	flex-direction: column;
	gap: 4px;
	z-index: 4;
}

.world-viewer-zoom-controls button {
	width: 32px;
	height: 32px;
	font-size: 16px;
	line-height: 1;
	cursor: pointer;
}

.world-viewer-tooltip {
	position: absolute;
	pointer-events: none;
	padding: 6px 10px;
	font-size: 12px;
	white-space: nowrap;
	z-index: 5;
}

.world-viewer-legend {
	display: flex;
	flex-wrap: wrap;
	gap: 8px 16px;
	font-size: 12px;
	color: var(--ink-muted);
}

.world-viewer-legend__item {
	display: inline-flex;
	align-items: center;
	gap: 6px;
}

.world-viewer-legend__swatch {
	width: 12px;
	height: 12px;
	border: 2px solid var(--panel-lo);
	flex-shrink: 0;
}

/* ---------------------------------------------------------------------- */
/* Debug page                                                             */
/* ---------------------------------------------------------------------- */

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

.debug-section__header {
	display: flex;
	align-items: baseline;
	gap: 8px;
}

.debug-section__title {
	margin: 0;
	font-size: 20px;
}

.debug-section__count {
	font-size: 12px;
}

.debug-search {
	font-size: 16px;
	padding: 8px 12px;
	max-width: 320px;
}

.debug-material-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
	gap: 10px;
}

.debug-material-item {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
	padding: 10px 8px;
	min-width: 0;
}

.debug-material-item__icon-wrap {
	width: 40px;
	height: 40px;
	display: flex;
	align-items: center;
	justify-content: center;
}

.debug-material-item__icon {
	width: 32px;
	height: 32px;
	object-fit: contain;
	image-rendering: pixelated;
}

.debug-material-item__label {
	font-size: 12px;
	color: var(--ink-muted);
	text-align: center;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	max-width: 100%;
}

.debug-biome-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
	gap: 8px;
}

.debug-biome-item {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 8px 10px;
	min-width: 0;
}

.debug-biome-item__swatch {
	width: 28px;
	height: 28px;
	border: 2px solid var(--panel-lo);
	flex-shrink: 0;
}

.debug-biome-item__labels {
	min-width: 0;
}

.debug-biome-item__name {
	font-size: 12px;
	color: var(--ink);
}

.debug-biome-item__meta {
	font-size: 12px;
	color: var(--ink-muted);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

@media (max-width: 720px) {
	.inspector-citizen-grid {
		grid-template-columns: 1fr;
	}
}
