﻿@import url('https://fonts.googleapis.com/css2?family=Uncial+Antiqua&family=Playfair+Display:ital@1&display=swap');

:root {
	/* Typography */
	--font-ui: 'Segoe UI', sans-serif;
	--font-titles: 'Uncial Antiqua', serif;
	/* Light Mode Colors */
	--bg: url(/ui/background_texture.webp);
	--color-bg-soft: #f3f2ef;
	--color-surface: #e7dec8;
	--color-surface-open: #e1d6bb;
	--color-text-main: #222222;
	--color-text-muted: #2c2c2c;
	--color-link: #333;
	--color-default-active: #ffa733;
}

/* A page takes its world's accent; pages outside a world keep the default. The realm classes below are more specific and still win. */
body {
	--color-active: var(--world-accent, var(--color-default-active));
	--color-active-soft: color-mix(in srgb, var(--color-active) 20%, transparent);
}

body.the-anchored {
	--color-active: green;
	--color-active-soft: rgba(0, 128, 0, 0.2); /* soft green */
}

body.the-dense {
	--color-active: black;
	--color-active-soft: rgba(0, 0, 0, 0.1); /* soft black (light gray) */
}

body.the-fractured-keep {
	--color-active: #ffa733;
	--color-active-soft: rgba(255, 167, 51, 0.2); /* soft orange */
}

@media (prefers-color-scheme: dark) {
	:root {
		--bg: #090909;
		--color-bg-soft: #1a1a1a;
		--color-surface: #2a2a2a;
		--color-surface-open: #3a3a3a;
		--color-text-main: #aaaaaa;
		--color-text-muted: #aaaaaa;
		--color-link: #aaaaaa;
		--color-default-active: #ffb347;
	}

	/* Accents are picked for the light parchment; lift them off the near-black ground. */
	body {
		--color-active: color-mix(in srgb, var(--world-accent, var(--color-default-active)) 75%, white);
	}

	body.the-dense {
		--color-active: #bbbbbb;
		--color-active-soft: #3a3a3a; /* soft black (light gray) */
	}

	body.the-fractured-keep {
		--color-active: #525390;
		--color-active-soft: #3a3a3a; /* soft black (light gray) */
	}
}



* {
	box-sizing: border-box;
}

body {
	margin: 0;
	padding: 0;
	font-family: var(--font-ui);
	background: var(--bg);
}

.row {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: center;
	background-repeat: no-repeat;
	background-size: contain;
}

.top-image {
	z-index: -1;
	position: absolute;
	width: 100%;
	top: 0;
	-webkit-mask-image: url(/ui/mask.webp);
	mask-image: url(/ui/mask.webp);
	mask-repeat: no-repeat;
	mask-size: contain;
}

li {
	color: var(--color-text-main);
}


/* Full-width header and footer */
header {
	flex-direction: row;
	width: 100%;
	color: var(--color-active);
	font-size: 32px;
	display: flex;
	align-items: center;
	gap: 1rem;
	padding: 1rem 2rem;
	font-family: var(--font-titles);
	text-transform: capitalize;
	text-align: center;
}

	header img {
		height: 100px;
		margin: 10px;
	}

footer {
	width: 100%;
	color: var(--color-text-main);
	font-size: 32px;
	padding: 1rem;
	text-align: center;
}

/* Scrollbars tinted to the surface instead of the browser default slab. */
* {
	scrollbar-width: thin;
	scrollbar-color: var(--color-surface-open) transparent;
}

::-webkit-scrollbar {
	width: 8px;
	height: 8px;
}

::-webkit-scrollbar-track {
	background: transparent;
}

::-webkit-scrollbar-thumb {
	background-color: var(--color-surface-open);
	border-radius: 4px;
	border: 2px solid transparent;
	background-clip: content-box;
}

::-webkit-scrollbar-thumb:hover {
	background-color: var(--color-active);
	background-clip: content-box;
}

::-webkit-scrollbar-corner {
	background: transparent;
}

aside {
	grid-area: aside;
	justify-self: start;
	width: 100%;
	align-self: start;
	position: sticky;
	top: 1rem;
	max-height: calc(100vh - 2rem);
	overflow-y: auto;
	font-size: 0.9rem;
	/* The panel keeps its scrollbar hidden until pointed at, so the column
	   edge stays clean while reading. */
	scrollbar-color: transparent transparent;
	transition: scrollbar-color 0.25s ease;
}

aside:hover,
aside:focus-within {
	scrollbar-color: var(--color-surface-open) transparent;
}

aside::-webkit-scrollbar-thumb {
	background-color: transparent;
}

aside:hover::-webkit-scrollbar-thumb,
aside:focus-within::-webkit-scrollbar-thumb {
	background-color: var(--color-surface-open);
	background-clip: content-box;
}

.relations h2 {
	font-family: var(--font-titles);
	font-size: 1.05rem;
	color: var(--color-active);
	border-bottom: 3px solid var(--color-active);
	margin: 0 0 1rem;
	padding-bottom: 0.3rem;
	display: block;
	width: 100%;
}

.relations .relation {
	margin-bottom: 1.2rem;
}

.relations .relation h3 {
	font-family: var(--font-ui);
	font-size: 0.7rem;
	letter-spacing: 0.09em;
	text-transform: uppercase;
	color: var(--color-text-muted);
	border-bottom: 1px solid var(--color-surface-open);
	margin: 0 0 0.35rem;
	padding-bottom: 0.2rem;
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 0.5rem;
	width: 100%;
	opacity: 0.85;
}

.relations .count {
	font-size: 0.65rem;
	opacity: 0.6;
	font-variant-numeric: tabular-nums;
}

.relations ul {
	list-style: none;
	margin: 0;
	padding: 0;
}

.relations li {
	margin: 0;
}

.relations a {
	display: block;
	color: var(--color-link);
	text-decoration: none;
	padding: 0.2rem 0.4rem;
	border-left: 2px solid transparent;
	line-height: 1.35;
}

.relations a:hover {
	color: var(--color-active);
	border-left-color: var(--color-active);
	background: var(--color-active-soft);
}

/* Short links side by side, such as session numbers, so a character in every session does not
   bury the rest of the panel under one long list. */
.relations .relation.compact ul {
	display: flex;
	flex-wrap: wrap;
	gap: 0.3rem;
}

.relations .relation.compact a {
	display: inline-block;
	min-width: 2.2rem;
	text-align: center;
	padding: 0.15rem 0.4rem;
	border: 1px solid var(--color-surface-open);
	border-radius: 3px;
	font-variant-numeric: tabular-nums;
}

.relations .relation.compact a:hover {
	border-color: var(--color-active);
}

/* A connection not everyone may know about. Only those who may see it get it at all, so this
   only reminds them not to say it aloud - the same mark as a secret block. */
.relations li.restricted > a {
	border-left: 3px solid var(--color-active);
	background: var(--color-bg-soft);
}

/* Main content container */
.container {
	display: grid;
	/* Same width and gutters as the top bar, so the columns line up under it edge to edge:
	   nav from its left end, aside to its right end, the article centred between them at
	   its reading width. */
	grid-template-columns: minmax(0, 22rem) minmax(0, 1fr) minmax(0, 15rem);
	grid-template-areas: "nav main aside";
	column-gap: 3rem;
	width: 100%;
	max-width: 1440px;
	/* No top: the heading row above ends in its own margin. */
	padding: 0 2rem 2rem;
}

/* Grid areas */
nav {
	grid-area: nav;
	justify-self: stretch;

	/* Never wider than the track it sits in. Without this one long child sizes the
	   whole column and spills into the gutter beside main. */
	max-width: 100%;
}

main {
	grid-area: main;
	justify-self: center;
	background: var(--bg);
	max-width: 700px;
	width: 100%;
}

	main img {
		position: relative;
		width: 100%;
		height: 100%;
		-webkit-mask-image: url(/ui/header-image-mask-full.webp);
		mask-image: url(/ui/header-image-mask-full.webp);
		mask-repeat: no-repeat;
		mask-size: 100% 100%;
		mask-position: center;
	}

/* Not enough width for three columns: panel moves under the article. */
@media (max-width: 1200px) {
	.container {
		grid-template-columns: minmax(0, 18rem) minmax(0, 1fr);
		grid-template-areas:
			"nav main"
			"nav aside";
		row-gap: 2.5rem;
	}

	aside {
		position: static;
		max-height: none;
		overflow: visible;
	}
}

/* Responsive: stack nav and main vertically. Scoped to the world page columns: a bare main {} here
   also reordered the portal, putting its footer above the worlds. */
@media (max-width: 768px), (pointer: coarse), (orientation: portrait) {
	.container {
		display: flex;
		flex-direction: column;
	}

	/* Each column brings its own side padding here. */
	.container {
		padding: 0 0 2rem;
	}

	.container > nav {
		order: 2;
		width: 100%;
		max-width: 100%;
		padding: 10px;
	}

	.container > main {
		order: 1;
		width: 100%;
		max-width: 100%;
		padding: 10px;
	}

	.container > aside {
		order: 3;
		width: 100%;
		max-width: 100%;
		padding: 10px;
		position: static;
		max-height: none;
		overflow: visible;
	}
}

h1, h2, h3, h4, h5, h6 {
	font-weight: normal;
	line-height: 1.3;
	margin-top: 2rem;
	margin-bottom: 1rem;
	border-bottom: 5px solid var(--color-active);
	width: 100%;
	display: inline-block;
	font-family: var(--font-titles);
	color: var(--color-active);
}


h1 {
	font-size: 3rem;
}

h2 {
	font-size: 2rem;
}

h3 {
	font-size: 1.5rem;
}

p {
	margin-bottom: 1.3rem;
	color: var(--color-text-main);
	line-height: 1.6;
}

	p > a {
		color: var(--color-active)
	}

		p > a:hover {
			color: var(--color-active);
		}

a {
	color: var(--color-text-main);
}

	a:hover {
		color: var(--color-active);
	}

	a.active {
		color: var(--color-active);
		font-weight: bold;
	}


nav ul {
	list-style: none;
	margin: 0;
	padding: 0;
}

	/* Indent child <ul>s progressively */
	nav ul ul {
		padding-left: 1.5em; /* This adds stacking indent per level */
	}

/* Folder summary */
li.folder > details > summary {
	display: flex;
	align-items: center;
	padding: 0.5rem 0.5rem;
	/*    background: #e7dec8;*/
	font-weight: bold;
	/*    color: #2c2c2c;*/
	border-left: 5px solid transparent;
	cursor: pointer;
	transition: background 0.2s, border-left 0.2s;
	background: var(--color-surface);
	color: var(--color-text-muted);
}

/* Open folders get red accent */
li.folder > details[open] > summary {
	border-left: 5px solid var(--color-active);
	/*background: #e1d6bb;*/
	background: var(--color-surface-open);
}

/* Folder icons */
li.folder > details > summary::before {
	content: "📁";
	margin-right: 0.5rem;
	color: #b58d0b;
}

li.folder.world > details > summary::before {
	content: "🌍" !important;
}

li.folder.plane > details > summary::before,
li.folder.realm > details > summary::before {
	content: "🌌" !important;
}

/*li.folder.region > details > summary::before {
    content: "🗻" !important;
}*/

li.folder > details[open] > summary::before {
	content: "📂";
}

/* Files */
li.file {
	display: flex;
	align-items: center;
	padding: 0.2rem 0.5rem 0.2rem 0.75rem;
	background-image: var(--bg)
}

	li.file::before {
		content: "📄";
		margin-right: 0.5rem;
		color: #999;
	}

	li.file a {
		text-decoration: none;
		color: var(--color-link);
		display: inline-block;
	}

		li.file a:hover {
			background-color: var(--bg);
		}

		li.file a.active {
			font-weight: bold;
			color: var(--color-active) !important;
			border-left: 4px solid var(--color-active);
			padding-left: 0.3rem;
			margin-left: -0.3rem;
		}


/* Nav mode toggle */
.nav-dir { display: none; }
body.dir-mode .nav-virtual { display: none; }
body.dir-mode .nav-dir { display: block; }

#nav-toggle {
	display: block;
	margin: 0.4rem 0 0.6rem;
	padding: 0.25rem 0.6rem;
	background: transparent;
	border: 1px solid var(--color-active);
	color: var(--color-active);
	cursor: pointer;
	font-family: var(--font-ui);
	font-size: 0.75rem;
	border-radius: 3px;
	width: 100%;
	text-align: left;
}

#nav-toggle:hover {
	background: var(--color-active-soft);
}

/* Table */
table {
	width: 100%;
	border-collapse: collapse;
	font-family: 'Segoe UI', sans-serif;
	font-size: 1rem;
	border: 1px solid #ccc;
	table-layout: fixed;
}

	table th,
	table td {
		padding: 0.75rem;
		text-align: left;
		border: 1px solid var(--color-bg-soft);
		color: var(--color-text-main) !important;

		/* The layout is fixed, so a word wider than its column spills out of the table and
		   scrolls the page sideways on a phone. Break it instead. */
		overflow-wrap: break-word;
	}

	table thead th {
		background-color: var(--color-active);
		color: white !important;
		font-weight: bold;
		border-bottom: 2px solid #ccc;
	}

		table thead th a {
			color: white !important;
		}

	table tbody tr {
		background: linear-gradient( to bottom, var(--color-active-soft) 0%, var(--color-bg-soft) 10%, var(--color-bg-soft) 90%, var(--color-active-soft) 100% );
		background-repeat: no-repeat;
		background-size: 100% 100%;
	}


hr {
	background-color: var(--color-active);
	height: 2px;
	border: none;
}


img {
	max-width: 100%;
}

/* Responsive */
@media (max-width: 768px) {
	html, body {
		flex-direction: column;
	}

	nav {
		width: 100%;
		height: auto;
	}
}




@font-face {
	font-family: MrsEavesSmallCaps;
	src: url(/ui/mrseavessmallcaps.ttf) format("truetype");
	font-weight: 100;
	font-style: normal
}

blockquote.dnd {
	background: url(/ui/stat-block-top-texture.webp),url(/ui/paper-texture.webp);
	background-size: 100% auto;
	background-position: top center;
	background-repeat: no-repeat,repeat;
	position: relative;
	box-shadow: 0 0 5px #979aa4;
	border: 1px solid #d4d0ce !important;
	padding: 15px;
	margin: 15px;
	font-family: "Scala Sans Offc",Roboto,Helvetica,sans-serif;
	font-size: 15px
}

	blockquote.dnd > hr {
		background-color: transparent !important;
		background: url(/ui/stat-block-header-bar.svg);
		background-repeat: no-repeat;
		min-height: 10px
	}

	blockquote.dnd > h1 {
		font-weight: 700;
		font-size: 34px !important;
		font-family: MrsEavesSmallCaps,Roboto,Helvetica,sans-serif !important;
		color: #822000 !important
	}

		blockquote.dnd > h1:after {
			display: none
		}

	blockquote.dnd > th {
		border-bottom: 2px solid #822000 !important
	}

	blockquote.dnd > table,
	blockquote.dnd > table td,
	blockquote.dnd > table th,
	blockquote.dnd > ul,
	blockquote.dnd > ul > li:before {
		color: #822000 !important;
		background-color: unset !important
	}

	blockquote.dnd > table {
		margin: 0 !important
	}

	blockquote.dnd > p {
		color: #000 !important
	}

		blockquote.dnd > p:first-child > img {
			position: absolute !important;
			right: 0;
			padding-right: 35px;
			max-width: 30%
		}

/* ---------------------------------------------------------------------------
   Portal: the world selection page at /
   --------------------------------------------------------------------------- */

body.portal {
	display: flex;
	flex-direction: column;
	min-height: 100vh;
	background: var(--bg);
	background-attachment: fixed;
}

/* One page width for everything that has to line up between the portal and a world: the top
   bar, the portal's tiles and a world's heading row. Each sets only its own top and bottom. */
.top-bar,
.portal-main,
.page-head {
	width: 100%;
	max-width: 1440px;
	margin-left: auto;
	margin-right: auto;
	padding-left: 2rem;
	padding-right: 2rem;
	box-sizing: border-box;
}

@media (max-width: 768px) {
	.top-bar,
	.portal-main,
	.page-head {
		padding-left: 1rem;
		padding-right: 1rem;
	}
}

.portal-main {
	flex: 1;
	padding-top: 2rem;
	padding-bottom: 3rem;
}

/* A world's first row: the way back, as a section heading in the very place the portal has
   "Campaigns". The columns follow on the heading's own bottom margin. */
.page-head {
	padding-top: 2rem;
}

	/* A link, not a heading: take the heading line height so it is the same size as one. */
	.page-back {
		text-decoration: none;
		line-height: 1.3;
	}

		.page-back:hover,
		.page-back:focus-visible {
			color: var(--color-active);
			opacity: 1;
		}

/* Search and the login. Page-level, outside the portal's tiles and a world's columns alike, so
   it sits in exactly the same place whichever page you are on. */
.top-bar {
	display: flex;
	flex-wrap: wrap;
	align-items: stretch;
	gap: 0.5rem 1rem;
	padding-top: 1.5rem;
}

@media (max-width: 768px) {
	.top-bar {
		padding-top: 1rem;
	}

	.portal-main,
	.page-head {
		padding-top: 1.5rem;
	}
}

	.top-bar .search {
		flex: 1 1 0;
		min-width: 12rem;
		margin: 0;
	}

	/* The badge is built to fill the nav column; here it only takes what it needs. */
	.top-bar .identity {
		width: auto;
		min-width: 0;
		margin: 0;
	}

	/* Stretched to the height of the search box beside it. */
	.top-bar .identity.guest {
		align-items: stretch;
	}

	.top-bar .identity.guest a {
		display: flex;
		align-items: center;
		padding: 0 1rem;
	}

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

.portal-header {
	text-align: center;
	margin-bottom: 3rem;
}

	.portal-header h1 {
		font-family: var(--font-titles);
		color: var(--color-active);
		font-size: clamp(2.5rem, 7vw, 4.5rem);
		margin: 0 0 0.5rem;
		text-transform: capitalize;
	}

	.portal-header p {
		color: var(--color-text-muted);
		font-size: 1.15rem;
		margin: 0;
	}

/* Three fixed steps rather than a fluid grid, so the cards never flip back and forth while
   the window is resized: one column of sideways cards, then two, then a row of four
   upright cards on a wide screen. */
.tiles {
	display: grid;
	grid-template-columns: 1fr;
	gap: 1.25rem;
}

@media (min-width: 760px) {
	.tiles {
		grid-template-columns: repeat(2, 1fr);
		gap: 1.5rem;
	}
}

@media (min-width: 1200px) {
	.tiles {
		grid-template-columns: repeat(4, 1fr);
		gap: 2rem;
	}

		.tiles-few > .tile {
			grid-column: span 2;
		}
}

.tile {
	display: block;
	background: var(--color-surface);
	border: 1px solid var(--color-surface-open);
	border-top: 4px solid var(--tile-accent, var(--color-active));
	border-radius: 4px;
	overflow: hidden;
	text-decoration: none;
	color: var(--color-text-main);
	transition: transform 0.18s ease, box-shadow 0.18s ease;
	box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18);
}

	.tile:hover,
	.tile:focus-visible {
		transform: translateY(-4px);
		box-shadow: 0 10px 24px rgba(0, 0, 0, 0.3);
		outline: none;
	}

.tile-inner {
	display: flex;
	flex-direction: column;
	height: 100%;
}

.tile-cover {
	aspect-ratio: 4 / 3;
	background-size: cover;
	background-position: center;
	background-color: var(--color-surface-open);
}

/* Sideways: picture on the left, the words beside it. Every card below a wide screen, and on
   a wide screen the stretched ones, so the campaigns look the same at every width. */
@media (max-width: 1199px) {
	.tile-inner {
		flex-direction: row;
	}

	.tile-cover {
		flex: 0 0 38%;
		aspect-ratio: auto;
		min-height: 10rem;
	}

	.tile-body {
		padding: 1rem 1.1rem 1.1rem;
	}

		.tile-body h2 {
			font-size: 1.35rem;
		}

	.tile-blurb {
		margin-top: 0.5rem;
		font-size: 0.98rem;
		line-height: 1.45;
	}
}

@media (min-width: 1200px) {
	.tiles-few .tile-inner {
		flex-direction: row;
	}

	.tiles-few .tile-cover {
		flex: 0 0 45%;
		aspect-ratio: auto;
		min-height: 17rem;
	}

	.tiles-few .tile-body {
		align-self: center;
		padding: 2rem 2.25rem;
	}
}


/* No art yet: a flat wash in the world's own colour rather than an empty box. */
.tile-cover-empty {
	background: linear-gradient(160deg, var(--tile-accent, var(--color-active)) 0%, var(--color-surface-open) 100%);
	opacity: 0.55;
}

.tile-body {
	padding: 1.25rem 1.5rem 1.5rem;
}

	.tile-body h2 {
		font-family: var(--font-titles);
		color: var(--tile-accent, var(--color-active));
		font-size: 1.6rem;
		margin: 0;
		line-height: 1.2;
	}

.tile-tagline {
	margin: 0.35rem 0 0;
	font-size: 0.8rem;
	letter-spacing: 0.09em;
	text-transform: uppercase;
	color: var(--color-text-muted);
	opacity: 0.7;
}

.tile-blurb {
	margin: 0.9rem 0 0;
	font-size: 0.95rem;
	line-height: 1.5;
	color: var(--color-text-muted);
}

/* ---------------------------------------------------------------------------
   Back to the portal, and the world's own header
   --------------------------------------------------------------------------- */

.portal-back {
	display: inline-block;
	margin: 0 0 0.5rem 2rem;
	font-size: 0.85rem;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--color-text-muted);
	text-decoration: none;
	opacity: 0.75;
}

	.portal-back:hover,
	.portal-back:focus-visible {
		opacity: 1;
		color: var(--world-accent, var(--color-active));
	}

header .world-home {
	display: flex;
	flex-direction: column;
	align-items: center;
	color: inherit;
	text-decoration: none;
	line-height: 1.1;
}

	header .world-home .tagline {
		font-family: var(--font-ui);
		font-size: 0.75rem;
		letter-spacing: 0.14em;
		text-transform: uppercase;
		color: var(--color-text-muted);
		margin-top: 0.4rem;
	}

/* Portal headings opt out of the article heading rule: no rules, no full-width bar. */
.portal-header,
.portal-header h1,
.tile-body h2 {
	display: block;
	border-bottom: none;
	width: auto;
	margin-top: 0;
}

.portal-header {
	flex-direction: column;
	padding: 0;
}

/* ---------------------------------------------------------------------------
   Portal groups: campaigns and the worlds they play in
   --------------------------------------------------------------------------- */

.portal-section {
	margin-bottom: 3.5rem;
}

.section-heading {
	font-family: var(--font-ui);
	font-size: 0.8rem;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--color-text-muted);
	opacity: 0.65;
	border-bottom: 1px solid var(--color-surface-open);
	padding-bottom: 0.6rem;
	margin: 0 0 1.75rem;
	width: auto;
	display: block;
}

/* Which world a campaign is played in, or which campaigns a world carries. */
.tile-setting {
	margin: 0.35rem 0 0;
	font-size: 0.8rem;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--color-text-muted);
	opacity: 0.7;
}

/* A connection whose other end is in a different world. */
.relation li.external > a {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 0.5rem;
}

	.relation li.external .world {
		font-size: 0.68rem;
		letter-spacing: 0.09em;
		text-transform: uppercase;
		opacity: 0.55;
		white-space: nowrap;
	}

	.relation li.external > a::after {
		content: "↗";
		display: none;
	}

/* ---------------------------------------------------------------------------
   Hover card: what a link points at, without following it
   --------------------------------------------------------------------------- */

.preview-card {
	position: absolute;
	z-index: 100;
	width: min(22rem, calc(100vw - 2rem));
	background: var(--color-surface);
	border: 1px solid var(--color-surface-open);
	border-top: 3px solid var(--world-accent, var(--color-active));
	border-radius: 4px;
	box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
	overflow: hidden;
	pointer-events: auto;
	animation: preview-in 0.12s ease-out;
}

@keyframes preview-in {
	from { opacity: 0; transform: translateY(-4px); }
	to   { opacity: 1; transform: none; }
}

.preview-image {
	display: block;
	width: 100%;
	height: 9rem;
	object-fit: cover;
	background: var(--color-surface-open);
}

.preview-body {
	padding: 0.85rem 1rem 1rem;
}

	.preview-body h4 {
		font-family: var(--font-titles);
		color: var(--world-accent, var(--color-active));
		font-size: 1.15rem;
		line-height: 1.2;
		margin: 0;
		border: none;
		width: auto;
		display: block;
	}

.preview-meta {
	margin: 0.3rem 0 0;
	font-size: 0.7rem;
	letter-spacing: 0.11em;
	text-transform: uppercase;
	color: var(--color-text-muted);
	opacity: 0.65;
}

.preview-desc {
	margin: 0.6rem 0 0;
	font-size: 0.86rem;
	line-height: 1.45;
	color: var(--color-text-muted);

	/* Long descriptions get trimmed rather than filling the screen. */
	display: -webkit-box;
	-webkit-line-clamp: 5;
	line-clamp: 5;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

@media (hover: none) {
	.preview-card { display: none; }
}

/* When a world has no virtual tree, the folder tree is the whole nav and is
   always shown - there is no toggle to hide it. */
.nav-dir.nav-only { display: block; }

/* ── Audience: who is looking, and what is being withheld ─────────────────── */

/* Which view you are in, at the top of the menu on every page. A player view
   that looks exactly like a dm view is how prep ends up on the wrong screen. */
.identity {
	/* The nav is width: max-content, and the max-content width of a wrapping flex row
	   is every item on ONE line - the whole sentence, the picker and the buttons side
	   by side. That let the badge size the entire column, and widened it further the
	   moment anything was added to it. Zero width contributes nothing to that
	   measurement; min-width fills whatever the nav's real content settles on. */
	width: 0;
	min-width: 100%;
	box-sizing: border-box;

	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 0.4rem 0.6rem;
	margin-bottom: 0.6rem;
	padding: 0.45rem 0.7rem;
	border-radius: 3px;
	font-family: var(--font-ui);
	font-size: 0.78rem;
	letter-spacing: 0.04em;
	background: var(--color-bg-soft);
	border: 1px solid var(--color-surface-open);
	color: var(--color-text-muted);
	line-height: 1.4;
}

/* The sentence is the one part that may wrap; everything else is a control. */
.identity .who {
	min-width: 0;
}

.identity .who strong {
	font-weight: 700;
	color: var(--color-text-main);
}

.identity a,
.identity button {
	margin-left: auto;
	white-space: nowrap;
	font: inherit;
	color: var(--color-link);
	background: none;
	border: 1px solid var(--color-surface-open);
	border-radius: 3px;
	padding: 0.15rem 0.6rem;
	cursor: pointer;
}

.identity a:hover,
.identity button:hover {
	border-color: var(--color-active);
	color: var(--color-active);
}

/* A guest gets only the button, tucked into the corner: no bar, no sentence. */
.identity.guest {
	justify-content: flex-end;
	padding: 0;
	background: none;
	border: none;
}

/* A dm looking through someone else's eyes. The picker row says so by changing, so
   the state is marked without a word of its own taking up a line - the badge lives in
   a 22rem column and every label in it is competing with the nav tree for width. */
.identity .view-as.worn {
	border-top-style: dashed;
	border-top-color: var(--color-active);
}

.identity .view-as.worn label {
	color: var(--color-active);
}

/* The dm's way into one of their own players' views. Its own row under the name, so
   the badge still reads as one sentence and the picker is not competing with it. */
.identity .view-as {
	order: 2;

	/* 0 0 100%, not flex-basis alone: a basis of 100% still shrinks by default, so the
	   picker would give way and settle back onto the first row. Refusing to shrink is
	   what makes it take a row of its own. */
	flex: 0 0 100%;
	min-width: 0;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.3rem;
	margin: 0.1rem 0 0;
	padding-top: 0.45rem;
	border-top: 1px solid var(--color-surface-open);
}

/* Log out keeps the end of the first row, above the picker rather than beside it. */
.identity > form:not(.view-as) {
	margin-left: auto;
}

.identity .view-as label {
	font-size: 0.68rem;
	text-transform: uppercase;
	letter-spacing: 0.07em;
	white-space: nowrap;
}

.identity .view-as select {
	font: inherit;
	font-size: 0.75rem;
	color: var(--color-text-main);
	background: var(--color-bg-soft);
	border: 1px solid var(--color-surface-open);
	border-radius: 3px;
	padding: 0.12rem 0.35rem;
	cursor: pointer;

	/* Takes the rest of its row and gives it back when the row is narrow, rather than
	   holding a width the nav would have to grow to fit. */
	flex: 1 1 6rem;
	min-width: 0;
	max-width: 100%;
}

.identity .view-as select:hover {
	border-color: var(--color-active);
}

/* The select submits itself where script runs, so this button is only the fallback
   for where it does not. Kept rather than dropped: no javascript, no dead end. */
.identity .view-as button {
	margin-left: 0;
	font-size: 0.7rem;
	padding: 0.1rem 0.4rem;
}

/* The dm view says so in the world's own accent; a character view is quieter. */
.identity.dm {
	background: var(--color-active-soft);
	border-color: var(--color-active);
}

.identity.dm .who strong {
	color: var(--color-text-main);
}

/* A block written for some viewers and not others. The contents of what is not
   for you never arrive; only a note that something exists, and for whom. What
   you may read sits in the flow of the page with a marker down its side. */
.secret {
	margin: 1.2rem 0;
	padding: 0.1rem 0 0.1rem 0.9rem;
	border-left: 3px solid var(--color-active);
}

.secret > .secret-label {
	display: block;
	font-family: var(--font-ui);
	font-size: 0.68rem;
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--color-active);
	margin-bottom: 0.3rem;
}

.secret > *:last-child { margin-bottom: 0; }

.secret.secret-withheld {
	border-left-style: dashed;
	padding: 0.5rem 0.9rem;
	font-family: var(--font-ui);
	font-size: 0.72rem;
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--color-text-muted);
}

/* A page written for someone else: named, and nothing more. */
.stub {
	box-sizing: border-box;
	max-width: 34rem;
	margin: 4rem auto;
	padding: 2.4rem 2rem;
	text-align: center;
	font-family: var(--font-ui);
	background: var(--color-surface);
	border: 1px solid var(--color-surface-open);
	border-radius: 6px;
	color: var(--color-text-main);
}

.stub h1 {
	font-family: var(--font-titles);
	color: var(--color-text-main);
}

.stub-action,
.stub-back {
	margin-top: 1.6rem;
	font-size: 0.9rem;
}

/* Login: one centred card, everything on the same axis. */
body.login-page {
	color: var(--color-text-main);
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	min-height: 100vh;
	margin: 0;
}

body.login-page footer {
	margin-top: 2rem;
	text-align: center;
}

body.login-page .portal-main {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	width: 100%;
	max-width: 24rem;
	margin: 0 auto;
	padding: 1.5rem;
	text-align: center;
}

body.login-page .portal-header {
	margin-bottom: 1.4rem;
	text-align: center;
}

body.login-page .portal-header h1 {
	margin: 0 0 0.4rem;
}

body.login-page .portal-header p {
	margin: 0;
	font-family: var(--font-ui);
	font-size: 0.86rem;
	color: var(--color-text-muted);
}

/* The card itself: square-ish, and every field the same width inside it. */
form.login {
	box-sizing: border-box;
	width: 100%;
	min-height: 21rem;
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 0.35rem;
	padding: 2rem 1.9rem;
	background: var(--color-surface);
	border: 1px solid var(--color-surface-open);
	border-radius: 6px;
	box-shadow: 0 2px 14px rgba(0, 0, 0, 0.12);
	font-family: var(--font-ui);
	color: var(--color-text-main);
	text-align: left;
}

form.login label {
	font-size: 0.72rem;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--color-text-muted);
	margin-top: 0.9rem;
}

form.login label:first-of-type {
	margin-top: 0;
}

form.login input {
	box-sizing: border-box;
	width: 100%;
	font: inherit;
	padding: 0.55rem 0.65rem;
	border: 1px solid var(--color-surface-open);
	border-radius: 3px;
	background: var(--color-bg-soft);
	color: var(--color-text-main);
}

form.login input:focus {
	outline: none;
	border-color: var(--color-active);
}

form.login button {
	box-sizing: border-box;
	width: 100%;
	margin-top: 1.6rem;
	font: inherit;
	letter-spacing: 0.04em;
	padding: 0.6rem;
	border: 1px solid var(--color-active);
	border-radius: 3px;
	background: var(--color-active-soft);
	color: var(--color-text-main);
	cursor: pointer;
}

form.login button:hover {
	background: var(--color-active);
	color: #1a1a1a;
	border-color: var(--color-active);
}

.login-error {
	margin: 0 0 0.6rem;
	padding: 0.55rem 0.7rem;
	border-left: 3px solid #b3392f;
	background: rgba(179, 57, 47, 0.12);
	border-radius: 0 3px 3px 0;
	font-size: 0.84rem;
	text-align: left;
}

.login-back {
	width: 100%;
	margin-top: 1.2rem;
	text-align: center;
	font-family: var(--font-ui);
	font-size: 0.85rem;
}


/* ---- Search ---------------------------------------------------------------
   One box in the nav, one on the portal, and a results page in portal chrome.
   Results are produced per request against the asking viewer, so a page written
   for someone else is not listed, not titled, and not quoted in a snippet. */

.nav-search {
	margin: 0 0 0.6rem;
}

.nav-search input {
	width: 100%;
	font: inherit;
	font-size: 0.8rem;
	font-family: var(--font-ui);
	color: var(--color-text-main);
	background: var(--color-bg-soft);
	border: 1px solid var(--color-surface-open);
	border-radius: 3px;
	padding: 0.3rem 0.5rem;
}

.nav-search input:focus,
.search input:focus {
	outline: none;
	border-color: var(--color-active);
}

.search {
	display: flex;
	gap: 0.5rem;
	margin: 0 0 1.5rem;
}

.search input {
	flex: 1 1 auto;
	min-width: 0;
	font: inherit;
	font-family: var(--font-ui);
	color: var(--color-text-main);
	background: var(--color-bg-soft);
	border: 1px solid var(--color-surface-open);
	border-radius: 3px;
	padding: 0.45rem 0.7rem;
}

.search button {
	font: inherit;
	font-family: var(--font-ui);
	color: var(--color-link);
	background: none;
	border: 1px solid var(--color-surface-open);
	border-radius: 3px;
	padding: 0.45rem 1rem;
	cursor: pointer;
}

.search button:hover {
	border-color: var(--color-active);
	color: var(--color-active);
}

.search-hint,
.search-count {
	font-family: var(--font-ui);
	font-size: 0.85rem;
	color: var(--color-text-muted);
	margin: 0 0 1.5rem;
}

.search-world {
	margin: 0 0 2rem;
}

.search-world h2 {
	font-family: var(--font-titles);
	font-size: 1.05rem;
	color: var(--color-active);
	border-bottom: 3px solid var(--color-active);
	margin: 0 0 0.8rem;
	padding-bottom: 0.3rem;
}

.search-world ul {
	list-style: none;
	margin: 0;
	padding: 0;
}

.search-world li {
	margin: 0 0 0.9rem;
	padding-left: 0.6rem;
	border-left: 2px solid transparent;
}

.search-world li:hover {
	border-left-color: var(--color-active);
}

.search-world a {
	font-family: var(--font-ui);
	font-size: 0.95rem;
	color: var(--color-link);
	text-decoration: none;
}

.search-world a:hover {
	color: var(--color-active);
}

/* The secrets review: filters as plain links, one entry per page with its blocks under it. */
.secrets-filter {
	display: flex;
	flex-wrap: wrap;
	gap: 0.3rem 0.9rem;
	margin: 0 0 0.8rem;
	font-family: var(--font-ui);
	font-size: 0.85rem;
}

.secrets-filter a {
	color: var(--color-link);
	text-decoration: none;
}

.secrets-filter a.active {
	color: var(--color-active);
	font-weight: 600;
	text-decoration: underline;
}

.secrets-page-entry {
	margin: 0 0 1.4rem;
}

.secrets-page-entry h3 {
	font-size: 1rem;
	margin: 0 0 0.5rem;
}

.secrets-page-entry .secret {
	margin: 0 0 0.6rem;
}

.search-kind {
	font-size: 0.65rem;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--color-text-muted);
	opacity: 0.7;
	margin-left: 0.5rem;
}

.search-snippet {
	display: block;
	font-family: var(--font-ui);
	font-size: 0.8rem;
	line-height: 1.5;
	color: var(--color-text-muted);
	margin-top: 0.15rem;
}

.search mark,
.search-snippet mark,
.search-world a mark {
	background: var(--color-active-soft);
	color: inherit;
	border-radius: 2px;
	padding: 0 0.1em;
}



/* ------------------------------------------------------------------------------------
   Dice. Notation in prose becomes a quiet inline button; a random table gets a roll
   button above it; results collect in a small tray in the bottom-left corner.
   ------------------------------------------------------------------------------------ */

.dice-roll {
	font: inherit;
	color: inherit;
	background: var(--color-active-soft);
	border: none;
	border-bottom: 1px dashed var(--color-active);
	border-radius: 2px;
	padding: 0 0.2em;
	cursor: pointer;
	white-space: nowrap;
}

.dice-roll:hover,
.dice-roll:focus-visible {
	background: var(--color-active);
	color: var(--color-bg-soft);
	outline: none;
}

.dice-table-roll {
	display: inline-block;
	margin: 0.5rem 0 0.4rem;
	font: 600 0.8rem var(--font-ui);
	letter-spacing: 0.04em;
	color: var(--color-text-main);
	background: var(--color-bg-soft);
	border: 1px solid var(--color-active);
	border-radius: 3px;
	padding: 0.25rem 0.7rem;
	cursor: pointer;
}

.dice-table-roll:hover {
	background: var(--color-active);
	color: white;
}

table tbody tr.dice-hit {
	background: var(--color-active);
	outline: 2px solid var(--color-active);
	transition: background 0.2s ease;
}

table tbody tr.dice-hit td {
	font-weight: 700;
}

.dice-tray {
	position: fixed;
	left: 1rem;
	bottom: 1rem;
	z-index: 900;
	min-width: 13rem;
	max-width: min(24rem, calc(100vw - 2rem));
	padding: 0.5rem 2rem 0.5rem 0.75rem;
	font: 0.85rem var(--font-ui);
	color: var(--color-text-main);
	background: var(--color-bg-soft);
	border: 1px solid var(--color-surface-open);
	border-left: 4px solid var(--color-active);
	border-radius: 4px;
	box-shadow: 0 4px 18px rgba(0, 0, 0, 0.25);
}

.dice-tray-close {
	position: absolute;
	top: 0.2rem;
	right: 0.3rem;
	font-size: 1.1rem;
	line-height: 1;
	color: var(--color-text-muted);
	background: none;
	border: none;
	cursor: pointer;
}

.dice-result {
	display: flex;
	align-items: baseline;
	gap: 0.5rem;
	padding: 0.15rem 0;
	opacity: 0.6;
}

.dice-result:first-of-type {
	opacity: 1;
	animation: dice-land 0.35s ease-out;
}

.dice-what {
	font-weight: 600;
	white-space: nowrap;
}

.dice-how {
	color: var(--color-text-muted);
	overflow-wrap: anywhere;
}

.dice-total {
	margin-left: auto;
	font-size: 1.15rem;
	font-family: var(--font-titles);
	color: var(--color-active);
}

.dice-result.dice-crit .dice-total::after { content: " \2605"; }
.dice-result.dice-fumble .dice-total { color: #b33; }

@keyframes dice-land {
	0% { transform: translateY(6px) rotate(-2deg); opacity: 0; }
	100% { transform: none; opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
	.dice-result:first-of-type { animation: none; }
}


/* ------------------------------------------------------------------------------------
   D&D Beyond. A character page opens with its sheet, read from the D&D Beyond file saved
   beside it: the character as built, laid out like the stat blocks. Links out to the
   site are marked.
   ------------------------------------------------------------------------------------ */

main a[href^="https://www.dndbeyond.com/"]::after {
	content: "\2009\2197";
	font-size: 0.8em;
	opacity: 0.6;
}

.sheet {
	--sheet-line: color-mix(in srgb, var(--color-active) 35%, var(--color-surface-open));
	margin: 0 0 1.5rem;
	padding: 1rem 1.1rem 0.75rem;
	font: 0.9rem var(--font-ui);
	color: var(--color-text-main);
	background: var(--color-bg-soft);
	border: 1px solid var(--color-surface-open);
	border-top: 4px solid var(--color-active);
	border-radius: 6px;
	box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}

.sheet-empty {
	padding: 0.6rem 1rem;
}

.sheet h3,
.sheet h4,
.sheet-name,
.sheet dt,
.sheet-ability-name,
.sheet-level span,
.sheet-section > summary {
	font-family: MrsEavesSmallCaps, var(--font-ui);
	letter-spacing: 0.04em;
}

/* Who: portrait, name, what they are, and their level in a seal on the right. */
.sheet-head {
	display: flex;
	align-items: center;
	gap: 0.9rem;
	padding-bottom: 0.8rem;
	border-bottom: 2px solid var(--sheet-line);
}

.sheet-portrait {
	width: 4.5rem;
	height: 4.5rem;
	flex: none;
	border-radius: 50%;
	object-fit: cover;
	border: 2px solid var(--color-active);
}

.sheet-who {
	flex: 1;
	min-width: 0;
}

.sheet-name {
	font-size: 1.7rem;
	line-height: 1.1;
	color: var(--color-active);
}

.sheet-line {
	margin-top: 0.15rem;
	font-weight: 600;
}

.sheet-origin {
	color: var(--color-text-muted);
	font-size: 0.82rem;
}

.sheet-level {
	flex: none;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	width: 3.6rem;
	height: 3.6rem;
	border: 2px solid var(--color-active);
	border-radius: 50%;
	font-size: 1.4rem;
	font-weight: 700;
	line-height: 1;
}

	.sheet-level span {
		font-size: 0.62rem;
		font-weight: 400;
		color: var(--color-text-muted);
		text-transform: lowercase;
	}

/* The numbers that stay put. */
.sheet-vitals {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
	gap: 0.5rem;
	margin: 0.9rem 0;
}

	.sheet-vitals > div {
		padding: 0.4rem 0.5rem;
		text-align: center;
		border: 1px solid var(--color-surface-open);
		border-radius: 4px;
	}

	.sheet-vitals dt {
		font-size: 0.78rem;
		color: var(--color-text-muted);
	}

	.sheet-vitals dd {
		margin: 0;
		font-size: 1.15rem;
		font-weight: 700;
	}

.sheet-abilities {
	display: grid;
	grid-template-columns: repeat(6, 1fr);
	gap: 0.5rem;
	margin-bottom: 1rem;
}

.sheet-ability {
	padding: 0.45rem 0.25rem 0.4rem;
	text-align: center;
	border: 1px solid var(--sheet-line);
	border-radius: 6px;
	background: color-mix(in srgb, var(--color-active) 4%, transparent);
}

.sheet-ability-name {
	font-size: 0.85rem;
	color: var(--color-text-muted);
}

.sheet-ability-mod {
	font-size: 1.6rem;
	font-weight: 700;
	line-height: 1.1;
}

.sheet-ability-score {
	display: inline-block;
	min-width: 2rem;
	margin: 0.1rem 0 0.35rem;
	padding: 0 0.4rem;
	font-size: 0.8rem;
	border: 1px solid var(--color-surface-open);
	border-radius: 999px;
}

.sheet-save {
	padding-top: 0.3rem;
	font-size: 0.75rem;
	color: var(--color-text-muted);
	border-top: 1px solid var(--color-surface-open);
}

	.sheet-save.is-proficient {
		color: var(--color-text-main);
		font-weight: 600;
	}

/* Proficiency as a dot: empty, half, full, or ringed for expertise. */
.sheet-dot {
	display: inline-block;
	width: 0.55rem;
	height: 0.55rem;
	margin-right: 0.35rem;
	border: 1px solid var(--color-text-muted);
	border-radius: 50%;
	vertical-align: 0.05em;
}

.is-half > .sheet-dot { background: linear-gradient(90deg, var(--color-active) 50%, transparent 50%); border-color: var(--color-active); }
.is-proficient > .sheet-dot { background: var(--color-active); border-color: var(--color-active); }
.is-expert > .sheet-dot { background: var(--color-active); border-color: var(--color-active); box-shadow: 0 0 0 2px var(--color-bg-soft), 0 0 0 3px var(--color-active); }

.sheet-adv {
	margin-left: 0.3rem;
	padding: 0 0.25rem;
	font-size: 0.65rem;
	font-weight: 700;
	text-decoration: none;
	color: var(--color-bg-soft);
	background: var(--color-active);
	border-radius: 3px;
	vertical-align: 0.15em;
	cursor: help;
}

.sheet-columns {
	display: grid;
	grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
	gap: 1rem;
}

.sheet h3 {
	margin: 0 0 0.4rem;
	padding-bottom: 0.2rem;
	font-size: 1.05rem;
	font-weight: 400;
	color: var(--color-active);
	border-bottom: 1px solid var(--sheet-line);
}

.sheet-skills {
	margin: 0;
	padding: 0;
	list-style: none;
	columns: 2;
	column-gap: 1rem;
}

	.sheet-skills li {
		display: flex;
		align-items: baseline;
		padding: 0.12rem 0;
		break-inside: avoid;
		border-bottom: 1px dotted var(--color-surface-open);
	}

	.sheet-skills li:not(.is-proficient):not(.is-expert) {
		color: var(--color-text-muted);
	}

.sheet-skill-name {
	flex: 1;
	min-width: 0;
}

	.sheet-skill-name small {
		font-size: 0.68rem;
		opacity: 0.7;
	}

.sheet-skill-bonus {
	font-weight: 600;
	font-variant-numeric: tabular-nums;
}

.sheet-facts {
	margin: 0;
}

	.sheet-facts dt {
		font-size: 0.82rem;
		color: var(--color-text-muted);
	}

	.sheet-facts dd {
		margin: 0 0 0.45rem;
	}

/* The long reads, folded away until asked for. */
.sheet-section {
	margin-top: 0.9rem;
	border-top: 1px solid var(--sheet-line);
}

	.sheet-section > summary {
		padding: 0.55rem 0 0.35rem;
		font-size: 1.05rem;
		color: var(--color-active);
		cursor: pointer;
	}

	.sheet-section > summary small {
		font-family: var(--font-ui);
		font-size: 0.72rem;
		color: var(--color-text-muted);
	}

	.sheet-section h4 {
		margin: 0.7rem 0 0.3rem;
		font-size: 0.9rem;
		font-weight: 400;
		color: var(--color-text-muted);
	}

	.sheet-section p {
		margin: 0 0 0.5rem;
		font-family: var(--font-prose, inherit);
		font-size: 1rem;
		line-height: 1.5;
	}

.sheet-features {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
	gap: 0.35rem 0.75rem;
	align-items: start;
}

.sheet-feature {
	border: 1px solid var(--color-surface-open);
	border-radius: 4px;
}

	.sheet-feature > summary {
		padding: 0.3rem 0.55rem;
		font-weight: 600;
		cursor: pointer;
	}

	.sheet-feature[open] {
		grid-column: 1 / -1;
		background: color-mix(in srgb, var(--color-active) 4%, transparent);
	}

	.sheet-feature[open] > summary {
		border-bottom: 1px solid var(--color-surface-open);
	}

	.sheet-feature > p {
		padding: 0 0.6rem;
	}

	.sheet-feature > p:first-of-type {
		padding-top: 0.45rem;
	}

.sheet-looks {
	display: flex;
	flex-wrap: wrap;
	gap: 0.4rem 1.25rem;
	margin: 0.3rem 0 0.4rem;
}

	.sheet-looks dt {
		font-size: 0.78rem;
		color: var(--color-text-muted);
	}

	.sheet-looks dd {
		margin: 0;
		font-weight: 600;
	}

.sheet-foot {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	gap: 0.25rem 1rem;
	margin-top: 0.8rem;
	padding-top: 0.5rem;
	font-size: 0.75rem;
	color: var(--color-text-muted);
	border-top: 1px solid var(--color-surface-open);
}

@media (max-width: 720px) {
	.sheet-columns {
		grid-template-columns: minmax(0, 1fr);
	}

	.sheet-abilities {
		grid-template-columns: repeat(3, 1fr);
	}
}

@media (max-width: 420px) {
	.sheet-head {
		flex-wrap: wrap;
	}

	.sheet-skills {
		columns: 1;
	}
}


/* ------------------------------------------------------------------------------------
   Local tools on a page: the edit link and the NPC chat. Only ever in a page served to
   a browser on the app's own machine.
   ------------------------------------------------------------------------------------ */

/* Bottom left: the top bar owns the top of the page and the npc chat the bottom right. */
.local-tools {
	position: fixed;
	bottom: 1rem;
	left: 1rem;
	z-index: 900;
}

.local-edit {
	display: inline-block;
	font: 600 0.85rem var(--font-ui);
	color: var(--color-text-main);
	text-decoration: none;
	background: var(--color-bg-soft);
	border: 1px solid var(--color-surface-open);
	border-left: 4px solid var(--world-accent, var(--color-active));
	border-radius: 4px;
	padding: 0.35rem 0.8rem;
	box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
}

.local-edit:hover {
	border-color: var(--color-active);
	color: var(--color-active);
}

.npc-chat {
	position: fixed;
	right: 1rem;
	bottom: 1rem;
	z-index: 900;
	width: min(26rem, calc(100vw - 2rem));
	font: 0.9rem var(--font-ui);
	color: var(--color-text-main);
	background: var(--color-bg-soft);
	border: 1px solid var(--color-surface-open);
	border-top: 4px solid var(--world-accent, var(--color-active));
	border-radius: 6px;
	box-shadow: 0 6px 24px rgba(0, 0, 0, 0.3);
}

.npc-chat.secrets-on {
	border-color: #b33;
	border-top-color: #b33;
	box-shadow: 0 0 0 2px rgba(187, 51, 51, 0.35), 0 6px 24px rgba(0, 0, 0, 0.3);
}

.npc-chat.collapsed {
	width: auto;
}

.npc-chat.collapsed .npc-chat-body {
	display: none;
}

.npc-chat-toggle {
	display: block;
	width: 100%;
	font: 600 0.9rem var(--font-ui);
	text-align: left;
	color: var(--color-text-main);
	background: none;
	border: none;
	padding: 0.5rem 0.8rem;
	cursor: pointer;
}

.npc-chat-body {
	display: flex;
	flex-direction: column;
	border-top: 1px solid var(--color-surface-open);
}

.npc-chat-log {
	max-height: min(50vh, 28rem);
	overflow-y: auto;
	padding: 0.6rem 0.8rem;
}

.npc-chat-intro {
	margin: 0 0 0.5rem;
	font-size: 0.8rem;
	color: var(--color-text-muted);
	font-style: italic;
}

.npc-chat-msg {
	margin: 0 0 0.7rem;
}

.npc-chat-who {
	display: block;
	font-size: 0.7rem;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--color-text-muted);
}

.npc-chat-msg.assistant .npc-chat-who {
	color: var(--color-active);
}

.npc-chat-text {
	white-space: pre-wrap;
	line-height: 1.5;
}

.npc-chat-msg.assistant .npc-chat-text {
	font-family: 'Playfair Display', Georgia, serif;
	font-style: italic;
	font-size: 1rem;
}

.npc-chat-msg.thinking .npc-chat-text::after {
	content: "\2026";
	animation: npc-think 1s steps(4) infinite;
}

.npc-chat-msg.failed .npc-chat-text {
	font-style: normal;
	font-family: var(--font-ui);
	color: #b33;
}

@keyframes npc-think {
	0% { opacity: 0.2; }
	50% { opacity: 1; }
	100% { opacity: 0.2; }
}

.npc-chat-form {
	padding: 0.5rem 0.8rem 0.7rem;
	border-top: 1px solid var(--color-surface-open);
}

.npc-chat-form textarea {
	width: 100%;
	resize: vertical;
	font: inherit;
	color: var(--color-text-main);
	background: var(--color-surface);
	border: 1px solid var(--color-surface-open);
	border-radius: 3px;
	padding: 0.4rem;
}

.npc-chat-buttons {
	display: flex;
	align-items: center;
	gap: 0.4rem;
	margin-top: 0.4rem;
}

.npc-chat-secrets {
	margin-right: auto;
	font-size: 0.8rem;
	color: var(--color-text-muted);
	cursor: pointer;
}

.npc-chat.secrets-on .npc-chat-secrets {
	color: #b33;
	font-weight: 700;
}

.npc-chat-buttons button,
.editor-page button {
	font: 600 0.8rem var(--font-ui);
	color: var(--color-text-main);
	background: var(--color-surface);
	border: 1px solid var(--color-surface-open);
	border-radius: 3px;
	padding: 0.3rem 0.75rem;
	cursor: pointer;
}

.npc-chat-buttons button:hover:not(:disabled),
.editor-page button:hover:not(:disabled) {
	border-color: var(--color-active);
}

.npc-chat-buttons button.primary,
.editor-page button.primary {
	color: white;
	background: var(--world-accent, var(--color-active));
	border-color: var(--world-accent, var(--color-active));
}

.editor-page button.danger {
	color: white;
	background: #b33;
	border-color: #b33;
}

.npc-chat-buttons button:disabled,
.editor-page button:disabled {
	opacity: 0.5;
	cursor: default;
}


/* ------------------------------------------------------------------------------------
   The editor: source on the left, preview / AI / problems on the right.
   ------------------------------------------------------------------------------------ */

body.editor-page {
	height: 100vh;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	color: var(--color-text-main);
}

.editor-bar {
	flex: none;
	display: flex;
	align-items: center;
	gap: 0.75rem;
	padding: 0.5rem 1rem;
	font: 0.9rem var(--font-ui);
	text-transform: none;
	text-align: left;
	color: var(--color-text-main);
	background: var(--color-bg-soft);
	border-bottom: 3px solid var(--world-accent, var(--color-active));
}

.editor-back {
	color: var(--color-link);
	text-decoration: none;
	white-space: nowrap;
}

.editor-back:hover { color: var(--color-active); }

.editor-where {
	display: flex;
	align-items: center;
	gap: 0.4rem;
	min-width: 0;
}

.editor-where strong {
	font-family: var(--font-titles);
	font-weight: normal;
	color: var(--color-active);
	white-space: nowrap;
}

.editor-sep { color: var(--color-text-muted); }

.editor-where select {
	min-width: 0;
	max-width: 40vw;
	font: 0.85rem ui-monospace, Consolas, monospace;
	color: var(--color-text-main);
	background: var(--color-surface);
	border: 1px solid var(--color-surface-open);
	border-radius: 3px;
	padding: 0.2rem 0.3rem;
}

.editor-spacer { flex: 1; }

.editor-status {
	font-size: 0.8rem;
	color: var(--color-text-muted);
	max-width: 30vw;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.editor-status.dirty { color: var(--color-active); }
.editor-status.ok { color: #3a8a4a; }
.editor-status.error { color: #b33; white-space: normal; }

.editor-main {
	flex: 1;
	min-height: 0;
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}

.editor-source {
	min-height: 0;
	display: flex;
	border-right: 1px solid var(--color-surface-open);
}

.editor-source textarea {
	flex: 1;
	resize: none;
	border: none;
	outline: none;
	padding: 1rem 1.25rem;
	font: 0.92rem/1.6 ui-monospace, 'Cascadia Code', Consolas, monospace;
	tab-size: 4;
	color: var(--color-text-main);
	background: var(--color-bg-soft);
}

.editor-side {
	min-height: 0;
	display: flex;
	flex-direction: column;
}

.editor-page nav.editor-tabs {
	flex: none;
	display: flex;
	gap: 0.25rem;
	padding: 0.4rem 0.6rem 0;
	width: auto;
	max-width: none;
	justify-self: auto;
	border-bottom: 1px solid var(--color-surface-open);
}

.editor-page .editor-tabs button {
	border-bottom: none;
	border-radius: 3px 3px 0 0;
	background: transparent;
}

.editor-page .editor-tabs button[aria-selected="true"] {
	background: var(--color-bg-soft);
	border-color: var(--color-surface-open);
	color: var(--color-active);
}

.editor-tabs button.has-suggestion::after {
	content: "\2022";
	margin-left: 0.3rem;
	color: var(--color-active);
}

.editor-panel {
	flex: 1;
	min-height: 0;
	overflow-y: auto;
}

#tab-preview {
	overflow: hidden;
	display: flex;
}

#tab-preview[hidden],
.editor-panel[hidden] {
	display: none;
}

#preview {
	flex: 1;
	border: none;
	background: var(--bg);
}

body.editor-preview main {
	max-width: 760px;
	margin: 0 auto;
	padding: 1rem 1.5rem 3rem;
}

.preview-ok { color: #3a8a4a; font-family: var(--font-ui); }
.preview-error { color: #b33; font-family: var(--font-ui); }

.editor-problems {
	margin: 0;
	padding: 1rem 1rem 1rem 2rem;
	font-size: 0.85rem;
	line-height: 1.5;
}

.editor-problems li.none {
	list-style: none;
	color: #3a8a4a;
}

#tab-ai {
	padding: 0.8rem 1rem 2rem;
	font-size: 0.9rem;
}

.ai-commands {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
	gap: 0.5rem;
}

.editor-page button.ai-command {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 0.2rem;
	text-align: left;
	padding: 0.5rem 0.7rem;
	background: var(--color-bg-soft);
}

.editor-page button.ai-command.suggested {
	border-color: var(--color-active);
	box-shadow: inset 4px 0 0 var(--color-active);
}

.ai-command-label { font-weight: 700; }

.ai-command-hint {
	font-weight: normal;
	font-size: 0.75rem;
	color: var(--color-text-muted);
}

.ai-command.suggested .ai-command-label::after {
	content: " \00b7 suggested";
	font-weight: normal;
	font-size: 0.7rem;
	color: var(--color-active);
}

.ai-instruction {
	width: 100%;
	margin: 0.6rem 0;
	font: inherit;
	color: var(--color-text-main);
	background: var(--color-surface);
	border: 1px solid var(--color-surface-open);
	border-radius: 3px;
	padding: 0.4rem 0.5rem;
}

.ai-output {
	white-space: pre-wrap;
	line-height: 1.55;
	max-height: 40vh;
	overflow-y: auto;
}

.ai-output:empty { display: none; }

.ai-output.thinking:empty {
	display: block;
	color: var(--color-text-muted);
	font-style: italic;
}

.ai-output.thinking:empty::before {
	content: attr(data-label) " \2014 thinking\2026";
}

.ai-note { color: var(--color-text-muted); font-style: italic; }
.ai-error { color: #b33; }

.ai-actions h3 {
	margin: 1rem 0 0.4rem;
	font-family: var(--font-titles);
	font-weight: normal;
	color: var(--color-active);
}

.ai-buttons {
	display: flex;
	align-items: center;
	gap: 0.4rem;
	margin: 0.4rem 0;
}

.ai-recap {
	width: 100%;
	font: 0.85rem/1.5 ui-monospace, Consolas, monospace;
	color: var(--color-text-main);
	background: var(--color-surface);
	border: 1px solid var(--color-surface-open);
	border-radius: 3px;
	padding: 0.5rem;
}

.ai-edges,
.ai-pages {
	margin: 0;
	padding-left: 0;
	list-style: none;
}

.ai-edges li { margin: 0.3rem 0; }
.ai-edges li.invalid { opacity: 0.65; }
.ai-edges code { font-size: 0.82rem; }
.ai-edge-when, .ai-edge-note { color: var(--color-text-muted); font-size: 0.82rem; }
.ai-edge-problem { color: #b33; font-size: 0.8rem; }
.ai-pages li { margin: 0.25rem 0; }
.ai-pages a { color: var(--color-active); }

.diff {
	font: 0.8rem/1.45 ui-monospace, Consolas, monospace;
	border: 1px solid var(--color-surface-open);
	border-radius: 3px;
	overflow-x: auto;
	max-height: 60vh;
	overflow-y: auto;
}

.diff > div,
.diff-hunk > div:not(.diff-hunk-buttons) {
	white-space: pre-wrap;
	padding: 0 0.5rem;
}

.diff-same { color: var(--color-text-muted); }
.diff-skip { color: var(--color-text-muted); font-style: italic; background: var(--color-surface); }
.diff-del { background: rgba(187, 51, 51, 0.16); text-decoration: line-through; text-decoration-color: rgba(187, 51, 51, 0.5); }
.diff-del::before { content: "- "; }
.diff-add { background: rgba(58, 138, 74, 0.18); }
.diff-add::before { content: "+ "; }

.diff-hunk {
	border-left: 3px solid var(--color-active);
	margin: 0.2rem 0;
}

.diff-hunk.accepted { border-left-color: #3a8a4a; }
.diff-hunk.accepted .diff-del { display: none; }
.diff-hunk.rejected { border-left-color: #b33; }
.diff-hunk.rejected .diff-add { display: none; }
.diff-hunk.rejected .diff-del { background: none; text-decoration: none; color: var(--color-text-muted); }
.diff-hunk.rejected .diff-del::before { content: "  "; }

.diff-hunk-buttons {
	display: flex;
	gap: 0.3rem;
	padding: 0.2rem 0.5rem 0.3rem;
}

.editor-dialog {
	width: min(56rem, calc(100vw - 2rem));
	color: var(--color-text-main);
	background: var(--color-bg-soft);
	border: 1px solid var(--color-surface-open);
	border-top: 4px solid var(--world-accent, var(--color-active));
	border-radius: 6px;
	padding: 1rem 1.25rem;
	font: 0.9rem var(--font-ui);
}

.editor-dialog::backdrop { background: rgba(0, 0, 0, 0.45); }

.editor-dialog h2 {
	margin-top: 0;
	font-family: var(--font-titles);
	font-weight: normal;
	color: var(--color-active);
}

#new-page-dialog { width: min(30rem, calc(100vw - 2rem)); }

.editor-dialog label {
	display: flex;
	flex-direction: column;
	gap: 0.2rem;
	margin-bottom: 0.6rem;
	font-weight: 600;
}

.editor-dialog input,
.editor-dialog select {
	font: 0.9rem var(--font-ui);
	color: var(--color-text-main);
	background: var(--color-surface);
	border: 1px solid var(--color-surface-open);
	border-radius: 3px;
	padding: 0.35rem 0.5rem;
}

.editor-hint {
	font: 0.8rem ui-monospace, Consolas, monospace;
	color: var(--color-text-muted);
}

.editor-hint.error { color: #b33; font-family: var(--font-ui); }

.editor-dialog-buttons {
	display: flex;
	justify-content: flex-end;
	gap: 0.5rem;
	margin-top: 0.8rem;
}

.editor-missing {
	max-width: 40rem;
	margin: 4rem auto;
	padding: 0 1rem;
}

@media (max-width: 900px) {
	.editor-main { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) minmax(0, 1fr); }
	.editor-source { border-right: none; border-bottom: 1px solid var(--color-surface-open); }
	.editor-status { max-width: 40vw; }
}

/* The way into the world graph, under the world's name. The graph page itself is styled in graph.css. */
.graph-link-nav {
	display: block;
	/* The same room below it the tree toggle leaves, so a world with no toggle does not press the link onto its tree. */
	margin: 0.4rem 0 0.6rem;
	padding: 0.25rem 0.6rem;
	border: 1px solid var(--world-accent, var(--color-active));
	border-radius: 3px;
	color: var(--world-accent, var(--color-active));
	font-size: 0.75rem;
	text-decoration: none;
}

	.graph-link-nav:hover,
	.graph-link-nav:focus-visible {
		background: var(--color-active-soft);
		color: var(--world-accent, var(--color-active));
	}

/* The dm's health marker: small enough to ignore, there when a page needs a look. */
.health-link {
	display: block;
	margin: -0.3rem 0 0.6rem;
	padding: 0 0.6rem;
	font-size: 0.7rem;
	color: var(--color-text-muted);
	text-decoration: none;
	opacity: 0.7;
}

	.health-link::before {
		content: "\25CF\00A0";
		color: #4c8c4a;
	}

	.health-link.health-warn::before {
		color: #c7862b;
	}

	.health-link:hover,
	.health-link:focus-visible {
		opacity: 1;
	}

	.graph-link-nav + #nav-toggle {
		margin-top: 0;
	}

/* ---- Phone menu -----------------------------------------------------------
   On a narrow or touch screen the nav leaves the page and becomes a drawer behind
   a sticky bar. Gated on .has-drawer, which the page's script sets: without script
   the nav stays in the page, stacked under the article, and nothing is out of reach. */

.mobile-bar,
.nav-close,
.nav-backdrop {
	display: none;
}

@media (max-width: 768px), (pointer: coarse), (orientation: portrait) {
	.has-drawer .mobile-bar {
		position: sticky;
		top: 0;
		z-index: 950;
		display: flex;
		align-items: center;
		gap: 0.75rem;
		padding: 0.5rem 1rem;
		padding-top: max(0.5rem, env(safe-area-inset-top));
		background: var(--color-bg-soft);
		border-bottom: 2px solid var(--color-active);
		box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
	}

	/* Just the three lines: no word, no outline. Still a 44px target for a thumb. */
	.menu-button {
		flex: none;
		display: inline-flex;
		align-items: center;
		justify-content: center;
		min-width: 44px;
		min-height: 44px;
		padding: 0;
		color: var(--color-active);
		background: transparent;
		border: none;
		cursor: pointer;
	}

	.mobile-world {
		flex: 1;
		min-width: 0;
		overflow: hidden;
		white-space: nowrap;
		text-overflow: ellipsis;
		font-family: var(--font-titles);
		font-size: 1.25rem;
		color: var(--color-active);
		text-decoration: none;
		text-transform: capitalize;
	}

	.has-drawer nav {
		position: fixed;
		top: 0;
		bottom: 0;
		left: 0;
		z-index: 1000;
		width: min(85vw, 22rem);
		max-width: none;
		margin: 0;
		padding: 3.5rem 1rem 1.5rem;
		padding-bottom: max(1.5rem, env(safe-area-inset-bottom));
		overflow-y: auto;
		overscroll-behavior: contain;
		background-color: var(--color-bg-soft);
		background-image: var(--bg);
		box-shadow: 4px 0 20px rgba(0, 0, 0, 0.35);
		transform: translateX(-100%);
		/* Hidden, not just moved off screen, so a closed drawer is out of the tab order too. */
		visibility: hidden;
		transition: transform 0.25s ease, visibility 0s linear 0.25s;
	}

	.has-drawer.nav-open nav {
		transform: none;
		visibility: visible;
		transition: transform 0.25s ease;
	}

	/* The connections panel: the same drawer, from the other side. */
	/* The menu button's twin: an icon and the count, no word, no outline. */
	.links-button {
		flex: none;
		display: inline-flex;
		align-items: center;
		justify-content: center;
		gap: 0.35rem;
		min-width: 44px;
		min-height: 44px;
		padding: 0 0.25rem;
		font: 600 1rem var(--font-ui);
		color: var(--color-active);
		background: transparent;
		border: none;
		cursor: pointer;
	}

		.links-button .count {
			font-size: 0.75rem;
			font-variant-numeric: tabular-nums;
			padding: 0.05rem 0.4rem;
			border-radius: 999px;
			background: var(--color-active-soft);
		}

	.has-links aside {
		position: fixed;
		top: 0;
		bottom: 0;
		right: 0;
		left: auto;
		z-index: 1000;
		width: min(85vw, 20rem);
		max-width: none;
		max-height: none;
		margin: 0;
		padding: 3.5rem 1rem 1.5rem;
		padding-bottom: max(1.5rem, env(safe-area-inset-bottom));
		overflow-y: auto;
		overscroll-behavior: contain;
		font-size: 1rem;
		background-color: var(--color-bg-soft);
		background-image: var(--bg);
		box-shadow: -4px 0 20px rgba(0, 0, 0, 0.35);
		scrollbar-color: var(--color-surface-open) transparent;
		transform: translateX(100%);
		visibility: hidden;
		transition: transform 0.25s ease, visibility 0s linear 0.25s;
	}

	.has-links.aside-open aside {
		transform: none;
		visibility: visible;
		transition: transform 0.25s ease;
	}

	.has-links aside .relations a {
		padding: 0.55rem 0.5rem;
	}

	.has-drawer .nav-close {
		display: block;
		position: absolute;
		top: 0.5rem;
		right: 0.5rem;
		width: 44px;
		height: 44px;
		font-size: 1.8rem;
		line-height: 1;
		color: var(--color-text-main);
		background: transparent;
		border: none;
		cursor: pointer;
	}

	.has-drawer .nav-backdrop {
		display: block;
		position: fixed;
		inset: 0;
		z-index: 999;
		background: rgba(0, 0, 0, 0.45);
		opacity: 0;
		pointer-events: none;
		transition: opacity 0.25s ease;
	}

	.has-drawer.nav-open .nav-backdrop,
	.has-drawer.aside-open .nav-backdrop {
		opacity: 1;
		pointer-events: auto;
	}

	/* The page behind an open drawer stays put. */
	.has-drawer.nav-open,
	.has-drawer.aside-open {
		overflow: hidden;
	}

	/* Finger-sized targets, drawer or not. */
	nav header {
		padding: 0.5rem 0;
		font-size: 26px;
	}

	nav header img {
		height: 64px;
	}

	.portal-back {
		margin: 0 0 0.5rem;
		padding: 0.5rem 0;
	}

	/* 16px: anything smaller and iOS zooms the page when the box is tapped. */
	.nav-search input,
	.top-bar .search input {
		font-size: 16px;
		padding: 0.6rem 0.7rem;
	}

	#nav-toggle,
	.graph-link-nav {
		padding: 0.65rem 0.8rem;
		font-size: 0.9rem;
	}

	li.folder > details > summary {
		min-height: 44px;
		padding: 0.6rem 0.6rem;
	}

	li.file {
		padding: 0 0.5rem 0 0.75rem;
	}

		li.file a {
			flex: 1;
			padding: 0.6rem 0;
		}

	.identity a,
	.identity button {
		padding: 0.4rem 0.8rem;
	}

	.identity .view-as select {
		font-size: 16px;
		padding: 0.35rem 0.5rem;
	}
}

@media (prefers-reduced-motion: reduce) {
	.has-drawer nav,
	.has-links aside,
	.has-drawer .nav-backdrop {
		transition: none !important;
	}
}
