/* ============================================================================
   BREAKPOINTS (slice 0044)
   ----------------------------------------------------------------------------
   The whole app uses THREE widths. Do not invent a fourth -- before 0044 there
   were nine, and components changed shape at different widths as a result.
   `scripts/verify-responsive.js` fails the build on a stray value.

       40rem =  640px   small-phone tweaks
       60rem =  960px   THE ONE THAT MATTERS: one column, sidebar -> drawer
       80rem = 1280px   reserved for wide-desktop

   These are comments, not custom properties, for two reasons:
     1. Custom properties DO NOT WORK inside a `@media` condition. `@media
        (max-width: var(--bp-md))` silently never matches.
     2. `rem` in a media query is always 16px -- it ignores the document's root
        font-size. `html` is 14px here, so 60rem is 960px in the query but
        840px if you write it in a rule. Query and rule do not agree; use px
        inside rules when you mean a rule.
   ========================================================================= */

:root {

    --color-1-darkest: #411716;
    --color-1-darker: #471817;
    --color-1:#4E1919; /* Night Bordeux */    
    --color-1-lighter: #5E2C2A;
    --color-1-lightest: #6F3F3C;

    --color-21:#EEE0CB; /* Champagne Mist */
    
    --color-2-darkest: #C4C1C1;
    --color-2-darker: #DCD9D9;
    --color-2:#F5F2F2; /* White Smoke */
    --color-2-lighter: #F6F3F3;
    --color-2-lighest: #F7F4F4;

    --color-31:#BAA898; /* Khaki Beige */

    --color-3: #FF7F11; /* Vivid Tangerine */

    --color-4-darkest: #312F2F;
    --color-4-darker: #363333;
    --color-4: #3B3838; /* Graphite */
    --color-4-lighter:#4B4848;
    --color-4-lighest: #5B5858;


    --color-5: #1A1D1B; /* Carbon Black */


}

.notepad {
    position: fixed;
    width:500px;
    padding:3rem;
    top:20rem;
    right:3rem;
    border:1rem solid #aa9069;
    background-color: var(--color-2);
}

* {
    font-size: 1rem;
    font-family: Roboto, Helvetica, sans-serif;
    font-family: Georgia, 'Times New Roman', Times, serif;
    box-sizing: border-box;
    margin: 0;
    text-decoration: none;
}

/* The `hidden` attribute's UA `display:none` has near-zero specificity, so any
   class that sets `display` (e.g. .admin-form__row { display:flex }) overrides
   it. Make hidden always win. */
[hidden] {
    display: none !important;
}

html {
    /* 14px everywhere. The old `clamp(13px, 1.6vw, 14px)` resolved to 13px on
       any phone (1.6vw of 390px is 6px), so the smallest screen got the
       smallest text -- backwards. rem is the multiplier for the entire app, so
       this moves in small steps only. */
    font-size: 14px;
}

/* iOS Safari ZOOMS THE PAGE when a focused form control is under 16px, and does
   not zoom back out -- the page is left oversized and scrolling sideways, which
   reads as "the site is broken". The control must be >=16px at the moment it
   receives focus, so this is keyed on a coarse pointer rather than a width.

   `!important` is deliberate and load-bearing. This is a hard constraint, not a
   preference, and a bare `input` selector (0,0,1) loses to ANY component rule --
   eight of them undercut it: Tom Select's vendor sheet hard-codes 13px AND loads
   after this file, `.article-infobox__table .infobox-input` is 0.85rem at (0,2,0),
   and the map stylesheets ship their own 13px. Without `!important` the floor
   silently applies only where nothing else has an opinion, which is precisely
   where it was never needed. */
@media (pointer: coarse) {
    input, select, textarea,
    .ts-control, .ts-control input, .ts-dropdown {
        font-size: max(16px, 1rem) !important;
    }
}

/* --- Overflow foundations (slice 0044) --------------------------------------
   Placed high in the cascade ON PURPOSE: these are floors, and any component
   below may override them. Deliberately NOT `overflow-x: hidden` on body --
   that hides the symptom, breaks `position: sticky` descendants, and makes the
   offending element impossible to find later. Fix the source instead. */

/* Media never widens its container. */
img, video, svg, canvas, iframe, embed, object {
    max-width: 100%;
}
img, video { height: auto; }

/* A flex/grid child's default `min-width: auto` refuses to shrink below its
   content, so ONE long word or wide table pushes the whole page sideways.
   This is the single most common cause of horizontal scroll in this layout. */
.dashboard__main, .topbar, .article-layout, .dash-card {
    min-width: 0;
}

/* Prose can contain URLs and invented names with no break opportunity. */
.article-body, .dash-card, .topbar__crumb {
    overflow-wrap: anywhere;
}

/* Wide content scrolls INSIDE its own box, never the page. `.table-scroll` is
   the wrapper for any <table> that can outgrow a phone -- see
   scripts/verify-responsive.js, which fails on an unwrapped one. */
.table-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    max-width: 100%;
}
.table-scroll > table { margin-bottom: 0; }

/* Touch targets. `hover: none` already covers a few components; this is the
   floor for the controls a phone user actually taps to get around. */
@media (pointer: coarse) {
    .topbar__actions a, .topbar__actions button,
    .sidebar__link, .folder-tree__link, .dash-card__cog, .dash-card__link {
        min-height: 44px;
    }
}

a {
    color:var(--color-1-lightest);
    font-weight: bold;
}

ul, ol {
	list-style: none;
	list-style-image: none;
	margin:0;
	padding:0;
}

html, body, footer {
    width:100%;
}

body {
    min-height:100vh;
    display:flex;
    flex-direction:column;
    /* No `justify-content: space-between`: it distributed sub-pixel free space as
       a gap above the footer (the "thin white line"). The middle region grows
       (flex:1) to fill instead, so the footer sits flush with no gap. */
    background-color: #fffcfc;
    background-image: url("/images/wallpaper/cream-paper.png");
    background-size: auto;
    background-repeat: repeat;
}


.header {
    width:100%;
    height:4rem;
    background-color:#8a3838;
    background-color:var(--color-1);
    color:#fafafa;
    display:flex;
    flex-direction: column;
    flex-shrink: 0;
}


.footer {
    width: 100%;
    min-height: 2rem;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    /* Content-sized (not a fixed 2rem the inner .container could spill past —
       the other source of the sliver below the footer). */
    background-color:#2a2a2a;
    color:#909090;
}

.flash {
    position:absolute;
    top:0;
    left:auto;
    padding: 10px;
    margin: 10px 0;
    border-radius: 4px;
}

.flash.success {
    background: #d4edda;
    color: #155724;
}

.flash.error {
    background: #f8d7da;
    color: #721c24;
}

.navbar {
    margin: 0;
    padding: 0;
    overflow: hidden;
    flex:1;
    display: grid;
    justify-content: center;
    grid-template-columns: 1fr 1fr 1fr;
    gap: clamp(0.5rem, 2vw, 5rem);
}

/* Drawer is `display: contents` on desktop so its children flow as direct
 * grid items of `.navbar` and pick up the column placement rules below.
 * On mobile it becomes a real box that overlays page content. */
.navbar__drawer { display: contents; }

.navbar__center {
    display:flex;
    justify-content: center;
    /*gap:5rem;*/
    height:100%;
    grid-column: 2;
}

.navbar__center a {
    flex:1;
    display:flex;
    background-color: #721e1e;
    background-color:var(--color-1-darker);
    justify-content: center;
}

.navbar__center a:hover {
    background-color:#581818;    
    background-color:var(--color-1-lightest);
}

.navbar__right {
    display:flex;
    justify-content: flex-end;
    height:100%;
    grid-column: 3;
}

.navbar__btn {
    height:100%;
    display:flex;
    align-items: center;
    padding:0 2rem 0 2rem;
    color:var(--color-2);
    text-decoration: 0px;
    font-weight: bold;
}

/* Hamburger + brand: hidden on desktop, visible on mobile. */
.navbar__menu-toggle {
    display: none;
    width: 44px;
    height: 44px;
    background: transparent;
    border: none;
    cursor: pointer;
    align-items: center;
    justify-content: center;
    color: inherit;
    padding: 0;
}
.navbar__menu-icon,
.navbar__menu-icon::before,
.navbar__menu-icon::after {
    display: block;
    width: 22px;
    height: 2px;
    background: currentColor;
    transition: transform 0.2s ease, opacity 0.2s ease;
}
.navbar__menu-icon { position: relative; }
.navbar__menu-icon::before,
.navbar__menu-icon::after {
    content: "";
    position: absolute;
    left: 0;
}
.navbar__menu-icon::before { top: -7px; }
.navbar__menu-icon::after  { top:  7px; }
.navbar--open .navbar__menu-icon { background: transparent; }
.navbar--open .navbar__menu-icon::before { transform: translateY(7px) rotate(45deg); }
.navbar--open .navbar__menu-icon::after  { transform: translateY(-7px) rotate(-45deg); }

.navbar__brand {
    display: none;
    color: inherit;
    text-decoration: none;
    font-weight: 700;
    font-size: 1.05rem;
    letter-spacing: 0.04em;
}


body {
    background-color: #fffcfc;
}

main {
    flex:1;
    min-height: 0;
    display:flex;
	justify-content: center;
}

.container {
    flex: 1;
    max-width: 50%;
    width: 50%;
    margin: auto;
    min-width: 800px;
    margin-top: 2rem;
    margin-bottom: 2rem;
    padding: 2rem;
}

.error {
    flex:1;
    display:flex;
    flex-direction: column;
    align-items: center;
    padding:2rem;
    /*background-color: #fffcfc;
    background-image: url("/images/wallpaper/cream-paper.png");*/
/* This is mostly intended for prototyping; please download the pattern and re-host for production environments. Thank you! */
}

.errorHeader {
    font-size:2.5rem;
}

.errorBody {
    font-size: 1.5rem;
}

.errorImage {
    background-image: url("/images/errors/404.png");
    width:512px;
    height:512px;
}









/* Characters */

.characters-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 1rem;
    margin-bottom: 1.5rem;
}

.characters-empty {
    text-align: center;
    color: var(--color-4-lighest);
    font-style: italic;
    padding: 3rem 1rem;
}

.characters-display {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 1.5rem;
}

.character-card {
    display: flex;
    flex-direction: column;
    background-color: var(--color-4);
    border: 1px solid var(--color-4-lighest);
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
    transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}

.character-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
    border-color: var(--color-3);
}

.character-card__top {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1.25rem;
    text-decoration: none;
    color: inherit;
}

.character-card .avatar-wrapper-small {
    flex: 0 0 auto;
    max-width: 4.5rem;
    max-height: 4.5rem;
}

.character-card .avatar.small {
    border-radius: 50%;
    border: 2px solid var(--color-4-lighest);
    object-fit: cover;
}

.character-card-information {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    color: var(--color-2);
}

.character-card__name {
    font-size: 1.5rem;
    font-weight: 600;
    line-height: 1.2;
    margin: 0;
}

.character-card__info {
    font-size: 0.9rem;
    color: var(--color-2-darkest);
    margin: 0;
}

.character-card__campaign {
    font-size: 0.85rem;
    color: var(--color-3);
    margin: 0.15rem 0 0;
}

.character-card__campaign em {
    font-style: italic;
}

.character-card__bottom {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 0.5rem;
    background-color: var(--color-4-darker);
    border-top: 1px solid var(--color-4-lighest);
    padding: 0.6rem 1rem;
}

.character-card__bottom > * {
    padding: 0.4rem 0.85rem;
    border-radius: 8px;
    font-size: 0.85rem;
    color: var(--color-2);
    text-decoration: none;
    background: transparent;
    border: 1px solid transparent;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

.character-card__bottom > *:hover {
    background-color: var(--color-4-lighest);
    border-color: var(--color-4-lighest);
}


.avatar-wrapper {
    display: block;
    position:relative;
    border:8px outset #645641;
    max-width: 150px;
    max-height: 150px;
    aspect-ratio: 1/1;
    overflow: visible;
}

.avatar-wrapper-small {
    display: block;
    position:relative;
    max-width: 4rem;
    max-height: 4rem;
    border: none;
    aspect-ratio: 1/1;
    overflow: visible;
        

}

.avatar-wrapper::before {
    content: "";
    position:absolute;
    top:0; left: 0; right: 0; bottom:0;
    -webkit-box-shadow: inset -5px -5px 20px 0px #000000; 
    box-shadow: inset -5px -5px 20px 0px #000000;
    pointer-events: none; /* ensures image is interactive */
}

.avatar {
    display: block;              /* Remove default inline spacing */
    height:100%;
    width:100%;

    &.small {        
        border-radius: 5px;
    }
}


/* Checkmark */
.checkbox {
	height:100%;
	appearance: none;
	border:1px solid #dadada;
	background-color: #fafafa;
	aspect-ratio: 1/1;
	transition: outline 2s;
	display:flex;
	align-items: center;
	justify-content: center;
	align-content: center;
	-webkit-box-shadow: inset 0px 0px 3px 0px #d8d8d8; 
	box-shadow: inset 0px 0px 3px 0px #d8d8d8;
	cursor: pointer;
}

.checkbox:checked::after {
	content:"";
	display:block;
	height:60%;
	width:60%;
	background-color:rgb(184, 66, 66);
	border-radius: 1px;
}


/*---------------------------------------------Compendium---------------------------------------------*/

.list-wrapper {
	max-width: 900px;
	width: 100%;
	padding: 2rem;
	margin: 0 auto;
	display: flex;
	flex-direction: column;
	flex: 1;
	min-height: 0;
}

/* Compendium fills the viewport between header (7rem) and footer (5rem)
   so its inner [data-list-body] can scroll while the search/filter bars
   stay pinned at the top. */
.list-wrapper[data-component="compendium"] {
	height: calc(100vh - 7rem - 5rem);
	max-height: calc(100vh - 7rem - 5rem);
}

/* Search bar */
.list-header-wrapper {
	background: var(--color-4);
	display: flex;
	align-items: center;
	padding: 0.75rem 1rem;
	gap: 0.75rem;
	border-radius: 8px;
	margin-bottom: 0.75rem;
}

.list-header-wrapper__label {
	font-size: 1rem;
	font-weight: 600;
	color: var(--color-2);
	white-space: nowrap;
}

.filter-input {
	padding: 0.5rem 0.75rem;
	font-size: 1rem;
	width: 100%;
	border: 1px solid var(--color-4-lighest);
	border-radius: 6px;
	background: var(--color-2);
	color: var(--color-5);
	outline: none;
	transition: border-color 0.15s ease;
}

.filter-input:focus {
	border-color: var(--color-3);
}

/* Filter pills */
.flat-list {
	display: flex;
	flex-wrap: wrap;
}

.filter-menu {
	gap: 0.5rem;
	margin-bottom: 0.5rem;
}

.filter-btn {
	border: 2px solid var(--color-4-lighter);
	background: transparent;
	color: var(--color-4);
	padding: 0.4rem 1rem;
	font-size: 0.85rem;
	font-weight: 600;
	border-radius: 20px;
	cursor: pointer;
	transition: all 0.15s ease;
	text-transform: uppercase;
	letter-spacing: 0.03em;
}

.filter-btn:hover {
	background: var(--color-4);
	color: var(--color-2);
	border-color: var(--color-4);
}

.filter-btn.active {
	background: var(--color-1);
	color: var(--color-2);
	border-color: var(--color-1);
}

.filter-btn:disabled {
	opacity: 0.3;
	cursor: not-allowed;
	border-style: dashed;
}

/* Subfilter row */
.subfilter-row {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-bottom: 1rem;
	padding: 0.5rem 0.25rem;
	border-top: 1px solid var(--color-4-lighter);
	align-items: center;
}

.subfilter-group {
	display: flex;
	flex-wrap: wrap;
	gap: 0.375rem;
	align-items: center;
	width: 100%;
}

.subfilter-subgroup {
	display: inline-flex;
	flex-wrap: wrap;
	gap: 0.375rem;
	align-items: center;
	margin-right: 0.75rem;
}

.subfilter-label {
	font-size: 0.7rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--color-4-lighest);
	margin-right: 0.25rem;
}

.subfilter-label--gap {
	margin-left: 0.75rem;
}

.filter-btn--sub {
	font-size: 0.75rem;
	padding: 0.25rem 0.75rem;
}

/* List layout */
.list-content {
	display: flex;
	flex-direction: column;
	flex: 1;
	min-height: 0;
	overflow: hidden;
}

.compendium-list {
	gap: 0;
	overflow-x: hidden;
}

/* Column headers */
.list-headers {
	display: grid;
	grid-template-columns: 2.5rem 2.5rem 1fr auto;
	align-items: center;
	padding: 0.5rem 1rem;
	border-bottom: 2px solid var(--color-31);
	margin-bottom: 0.5rem;
	flex-shrink: 0;
	min-width: 0;
}

.list-item__header {
	font-size: 0.75rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--color-4-lighest);
	padding: 0;
}

.list-item__header--sortable {
	cursor: pointer;
	user-select: none;
	transition: color 0.15s ease;
}

.list-item__header--sortable:hover {
	color: var(--color-3);
}

.list-item__header--sortable.sort-active {
	color: var(--color-1);
}

.list-item__header--sortable.sort-asc::after {
	content: ' ↑';
}

.list-item__header--sortable.sort-desc::after {
	content: ' ↓';
}

/* Star icon */
.icon-star {
	background-image: url('data:image/svg+xml,<svg fill="%23ebc954" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 126.729 126.73"><path d="M121.215,44.212l-34.899-3.3c-2.2-0.2-4.101-1.6-5-3.7l-12.5-30.3c-2-5-9.101-5-11.101,0l-12.4,30.3c-0.8,2.1-2.8,3.5-5,3.7l-34.9,3.3c-5.2,0.5-7.3,7-3.4,10.5l26.3,23.1c1.7,1.5,2.4,3.7,1.9,5.9l-7.9,32.399c-1.2,5.101,4.3,9.3,8.9,6.601l29.1-17.101c1.9-1.1,4.2-1.1,6.1,0l29.101,17.101c4.6,2.699,10.1-1.4,8.899-6.601l-7.8-32.399c-0.5-2.2,0.2-4.4,1.9-5.9l26.3-23.1C128.615,51.212,126.415,44.712,121.215,44.212z"/></svg>');
	background-size: contain;
	background-position: center;
	background-repeat: no-repeat;
	height: 20px;
	width: 20px;
	flex-shrink: 0;

	&.empty {
		background-image: url('data:image/svg+xml,<svg fill="none" stroke="%23BAA898" stroke-width="8" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 126.729 126.73"><path d="M121.215,44.212l-34.899-3.3c-2.2-0.2-4.101-1.6-5-3.7l-12.5-30.3c-2-5-9.101-5-11.101,0l-12.4,30.3c-0.8,2.1-2.8,3.5-5,3.7l-34.9,3.3c-5.2,0.5-7.3,7-3.4,10.5l26.3,23.1c1.7,1.5,2.4,3.7,1.9,5.9l-7.9,32.399c-1.2,5.101,4.3,9.3,8.9,6.601l29.1-17.101c1.9-1.1,4.2-1.1,6.1,0l29.101,17.101c4.6,2.699,10.1-1.4,8.899-6.601l-7.8-32.399c-0.5-2.2,0.2-4.4,1.9-5.9l26.3-23.1C128.615,51.212,126.415,44.712,121.215,44.212z"/></svg>');
	}
}

/* List body */
[data-list-body] {
	display: flex;
	flex-direction: column;
	gap: 0.4rem;
	overflow-y: auto;
	overflow-x: hidden;
	flex: 1;
	min-height: 0;
}

/* Entry cards */
.list-item {
	display: grid;
	grid-template-columns: 2.5rem 2.5rem 1fr auto;
	align-items: center;
	width: 100%;
	min-height: 3.5rem;
	padding: 0.6rem 1rem;
	background: var(--color-2);
	border-radius: 8px;
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
	border-left: 3px solid transparent;
	border-right: 3px solid transparent;
	transition: all 0.15s ease;
	box-sizing: border-box;
	min-width: 0;
}

/* Row icon */
.list-item__icon {
	width: 28px;
	height: 28px;
	border-radius: 4px;
	object-fit: cover;
	flex-shrink: 0;
}

.compendium-entry {
	cursor: pointer;
}

.compendium-entry:hover {
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
	border-left-color: var(--color-3);
	transform: translateX(2px);
}

/* Name styling in rows */
.list-item__name {
	font-size: 0.95rem;
	font-weight: 500;
	color: var(--color-5);
	margin: 0;
}

.list-item__desc {
    margin-left:0.5rem;
    font-size:0.8rem;
    color: var(--color-2-darkest);
}

/* Star button in rows */
.list-item__star {
	border: none;
	background-color: transparent;
	cursor: pointer;
	padding: 0;
	display: flex;
	align-items: center;
	justify-content: center;
}

.list-item__star:hover {
	transform: scale(1.2);
}

/* Empty state */
.compendium-empty {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 3rem 1rem;
	color: var(--color-4-lighest);
	font-style: italic;
	font-size: 0.95rem;
}

/* Type badges */
.list-item__type-badge {
	font-size: 0.7rem;
	font-weight: 700;
	padding: 0.2rem 0.6rem;
	border-radius: 10px;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	white-space: nowrap;
}

.list-item__type-badge--item,
.list-item__type-badge--container {
	background-color: var(--color-21);
	color: var(--color-1);
}

.list-item__type-badge--race {
	background-color: var(--color-1-lightest);
	color: var(--color-2);
}

.list-item__type-badge--spell {
	background-color: var(--color-31);
	color: var(--color-5);
}

.list-item__type-badge--rule {
	background-color: var(--color-4);
	color: var(--color-2);
}

/* Compendium Detail (modal partials) */
.compendium-detail {
	padding: 1.5rem;
}

.compendium-detail__name {
	font-size: 1.4rem;
	font-weight: 700;
	color: var(--color-5);
	margin-bottom: 0.75rem;
}

.compendium-detail__meta {
	display: flex;
	gap: 0.5rem;
	align-items: center;
	margin-bottom: 1.25rem;
	flex-wrap: wrap;
}

.compendium-detail__type-badge {
	font-size: 0.7rem;
	font-weight: 700;
	padding: 0.2rem 0.6rem;
	border-radius: 10px;
	text-transform: uppercase;
	letter-spacing: 0.04em;
}

.compendium-detail__type-badge--item {
	background-color: var(--color-21);
	color: var(--color-1);
}

.compendium-detail__type-badge--race {
	background-color: var(--color-1-lightest);
	color: var(--color-2);
}

.compendium-detail__type-badge--rule {
	background-color: var(--color-4);
	color: var(--color-2);
}

.compendium-detail__category,
.compendium-detail__subcategory {
	font-size: 0.8rem;
	color: var(--color-4-lighest);
	font-weight: 500;
}

.compendium-detail__description {
	margin-bottom: 1.25rem;
	line-height: 1.6;
	color: var(--color-4);
}



.compendium-detail__reverse-name {
	font-size: 1.1rem;
	font-weight: 600;
	color: var(--color-5);
	margin-bottom: 0.75rem;
}

.compendium-detail__image {
	margin-bottom: 1.25rem;
}

.compendium-detail__image img {
	max-width: 100%;
	border-radius: 6px;
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.compendium-detail__stats {
	width: 100%;
	border-collapse: collapse;
	margin-bottom: 1.25rem;
}

.compendium-detail__stats th,
.compendium-detail__stats td {
	padding: 0.5rem 0.75rem;
	border-bottom: 1px solid var(--color-2-darker);
	text-align: left;
	font-size: 0.9rem;
}

.compendium-detail__stats th {
	font-weight: 600;
	width: 40%;
	color: var(--color-4);
}

.compendium-detail__stats thead th {
	border-bottom: 2px solid var(--color-31);
	color: var(--color-5);
}

.compendium-detail__abilities {
	list-style: none;
	padding: 0;
	margin: 0;
}

.compendium-detail__abilities li {
	padding: 0.6rem 0;
	border-bottom: 1px solid var(--color-2-darker);
}

.compendium-detail__abilities li:last-child {
	border-bottom: none;
}

.compendium-detail__abilities strong {
	color: var(--color-5);
}

.compendium-detail__abilities p {
	margin-top: 0.25rem;
	color: var(--color-4-lighter);
	line-height: 1.5;
	font-size: 0.9rem;
}


/*---------------------------------------------Article---------------------------------------------*/

.article-layout {
	width: 100%;
	/*max-width: 1050px;*/
	margin: 2rem auto;
	padding: 0 2rem;
}

.article-layout__back {
	display: inline-block;
	margin-bottom: 1rem;
	color: var(--color-4);
	font-size: 0.85rem;
	font-weight: bold;
}
.article-layout__back:hover { color: var(--color-1); }

.article-header {
	border-bottom: 2px solid var(--color-31);
	padding-bottom: 0.75rem;
	margin-bottom: 1.25rem;
}
.article-header__title { font-size: 2rem; color: var(--color-5); margin: 0; line-height: 1.15; }
.article-header__type {
	font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.05em;
	color: var(--color-1); font-weight: 700; margin-top: 0.3rem;
}
.article-header__folder { margin-top: 0.5rem; display: flex; align-items: center; gap: 0.4rem; font-size: 0.8rem; }
.article-header__folder-label { text-transform: uppercase; letter-spacing: 0.05em; color: var(--color-1); font-weight: 700; }
.article-header__folder select { font-size: 0.85rem; padding: 0.2rem 0.4rem; }
.article-header__actions { margin-top: 0.85rem; display: flex; gap: 0.5rem; }

/* flow-root so the floated infobox is contained within the content block */
.article-content { 
    display: grid; 
    grid-template-columns: 9fr 3fr;

}

.article-body { color: var(--color-4-darkest); }
/* Multi-value select output: one value per line, no bullets. */
.article-infobox__list { list-style: none; margin: 0; padding: 0; }
.article-infobox__list li { padding: 0.05rem 0; }
/* What one particular link IS — "(CEO)" (slice 0037). Quieter than the link it
   follows: it qualifies the target, it is not part of its name. */
.article-infobox__note { color: var(--color-4-lighter); font-size: 0.9em; }

.article-body__empty { color: var(--color-4-lighter); font-style: italic; }

/* "Redirected from …" (slice 0041) — a footnote, not an announcement. */
.article-header__redirect {
	margin: 0.1rem 0 0; font-size: 0.82rem; font-style: italic;
	color: var(--color-4-lighter);
}

/* "What links here" (slice 0040). */
.backlinks { margin-top: 2rem; border-top: 1px solid var(--color-31); padding-top: 0.75rem; }
.backlinks__summary {
	cursor: pointer; font-size: 0.8rem; text-transform: uppercase;
	letter-spacing: 0.05em; color: var(--color-4-lighter);
}
.backlinks__count {
	display: inline-block; margin-left: 0.3rem; padding: 0 0.35rem;
	border-radius: 8px; background: var(--color-21); color: var(--color-4-darkest);
	font-size: 0.75rem; letter-spacing: 0;
}
.backlinks__list { list-style: none; margin: 0.6rem 0 0; padding: 0; }
.backlinks__item { margin: 0.2rem 0; }
.backlinks__kind {
	margin-left: 0.35rem; font-size: 0.72rem; text-transform: uppercase;
	letter-spacing: 0.04em; color: var(--color-4-lighter);
}

/* Wiki links (slice 0040). Three states, and the reader never sees the third
   as "broken": a page that doesn't exist is the world's business, not theirs. */
.page-link { color: var(--color-1); text-decoration: underline; text-underline-offset: 2px; }
.page-link:hover { text-decoration-thickness: 2px; }
/* Editors only: a link to a page nobody has written yet, pointing at the create
   form seeded with its path. */
/* --color-3 (Vivid Tangerine) rather than a red of its own: the palette has no
   danger token, and inventing one here would be the only hard-coded colour on
   the page. */
.page-link--new { color: var(--color-3); text-decoration-style: dashed; }
.page-link--new::after { content: '+'; font-size: 0.75em; vertical-align: super; margin-left: 0.1em; }
/* Readers: the same words, marked as going nowhere, explained on hover. */
.page-link--missing {
	color: inherit; text-decoration: underline dotted; text-underline-offset: 2px;
	cursor: help;
}
.article-body__empty a { font-style: normal; font-weight: 600; }
.article-body > *:first-child { margin-top: 0; }
.article-body p { margin-bottom: 1rem; }
.article-body h1, .article-body h2, .article-body h3 { color: var(--color-1); margin: 1.25rem 0 0.5rem; }
.article-body ul, .article-body ol { margin: 0 0 1rem 1.5rem; }
.article-body ul { list-style: disc; }
.article-body ol { list-style: decimal; }

/* --- Checklists -----------------------------------------------------------
   One rule set for BOTH the editor and the rendered page: the node serialises
   the same `li[data-state] > div` shape its node view builds, so a checklist
   cannot look like two different things.

   ⚠️ The box is drawn here, in CSS, from an inert `data-state` attribute.
   Nothing interactive is stored — `input` is not on the sanitizer's tag
   allowlist, and a checklist in a body must never be able to submit anything.

   Three states, cycled by clicking the box in the editor:
     open     empty box
     done     tick, text dimmed
     dropped  cross, text struck through
   On the page the boxes are display only; cycling happens in the editor. */
.article-body ul[data-type="taskList"],
.tiptap-editor__surface ul[data-type="taskList"] {
	list-style: none; margin-left: 0; padding-left: 0;
}
.article-body ul[data-type="taskList"] > li,
.tiptap-editor__surface ul[data-type="taskList"] > li {
	display: flex; align-items: flex-start; gap: 0.55rem;
	margin: 0 0 0.3rem;
}
.article-body ul[data-type="taskList"] > li > div,
.tiptap-editor__surface ul[data-type="taskList"] > li > div { flex: 1; min-width: 0; }
.article-body ul[data-type="taskList"] > li > div > p:last-child,
.tiptap-editor__surface ul[data-type="taskList"] > li > div > p:last-child { margin-bottom: 0; }

/* The box: an ::before on the page (there is no node view there), and a real
   span in the editor so it can be clicked. Both get the same look.

   ⚠️ The editor surface lives INSIDE `.article-body` (see the edit views), so
   the page rule matches there too and rendered a second, dead box beside the
   clickable one. Suppress it explicitly rather than trying to out-specify it. */
.article-body ul[data-type="taskList"] > li::before,
.tiptap-editor__surface ul[data-type="taskList"] > li > .task-box {
	content: ""; flex: none; box-sizing: border-box;
	width: 1.05em; height: 1.05em; margin-top: 0.2em;
	border: 1.5px solid var(--color-2-darkest, #8a8a8a); border-radius: 3px;
	background: transparent center / 0.8em 0.8em no-repeat;
}
.tiptap-editor__surface ul[data-type="taskList"] > li::before { content: none; }
.tiptap-editor__surface ul[data-type="taskList"] > li > .task-box { cursor: pointer; }
.tiptap-editor__surface ul[data-type="taskList"] > li > .task-box:focus-visible {
	outline: 2px solid var(--color-1, #6b8f71); outline-offset: 2px;
}

/* done — a tick, and the text recedes. `[data-checked="true"]` is the shape
   bodies had before the third state; they render correctly until next saved. */
.article-body ul[data-type="taskList"] > li[data-state="done"]::before,
.article-body ul[data-type="taskList"] > li[data-checked="true"]:not([data-state])::before,
.tiptap-editor__surface ul[data-type="taskList"] > li[data-state="done"] > .task-box {
	border-color: var(--color-1, #6b8f71);
	background-color: var(--color-1, #6b8f71);
	background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
}
.article-body ul[data-type="taskList"] > li[data-state="done"] > div,
.article-body ul[data-type="taskList"] > li[data-checked="true"]:not([data-state]) > div,
.tiptap-editor__surface ul[data-type="taskList"] > li[data-state="done"] > div {
	color: var(--color-2-darkest, #8a8a8a);
}

/* dropped — a cross, and the text is struck through. Grey rather than red: a
   line that was cut is not an error. */
.article-body ul[data-type="taskList"] > li[data-state="dropped"]::before,
.tiptap-editor__surface ul[data-type="taskList"] > li[data-state="dropped"] > .task-box {
	border-color: var(--color-2-darkest, #8a8a8a);
	background-color: var(--color-2-darkest, #8a8a8a);
	background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'/%3E%3Cline x1='6' y1='6' x2='18' y2='18'/%3E%3C/svg%3E");
}
.article-body ul[data-type="taskList"] > li[data-state="dropped"] > div,
.tiptap-editor__surface ul[data-type="taskList"] > li[data-state="dropped"] > div {
	text-decoration: line-through;
}
.article-body blockquote {
	border-left: 3px solid var(--color-31);
	padding-left: 1rem; margin: 0 0 1rem;
	color: var(--color-4-lighter); font-style: italic;
}
.article-body code {
	background: var(--color-2-darker);
	padding: 0.1em 0.35em; border-radius: 3px; font-family: monospace;
}
.article-body pre {
	background: var(--color-5); color: var(--color-2);
	padding: 1rem; border-radius: 6px; overflow-x: auto; margin-bottom: 1rem;
}
.article-body pre code { background: none; padding: 0; color: inherit; }
.article-body hr { border: none; border-top: 1px solid var(--color-31); margin: 1.5rem 0; }

/* Secret content (slice 0027): editor-only. Readers never receive it (the server
   strips it), so this styling is only ever seen by an editor — a clear "this
   won't be shown to readers" cue in both the rendered body and the editor.
   Block = a boxed section with a badge; inline = a subtly tinted span. */
.article-body div[data-secret],
.tiptap-editor__surface div[data-secret] {
	position: relative;
	border: 1px dashed var(--color-1);
	border-radius: 6px;
	background: color-mix(in srgb, var(--color-1) 7%, transparent);
	padding: 1.6rem 0.9rem 0.9rem;
	margin: 0 0 1rem;
}
.article-body div[data-secret]::before,
.tiptap-editor__surface div[data-secret]::before {
	content: "SECRET";
	position: absolute; top: 0; left: 0;
	font-size: 0.62rem; font-weight: 700; letter-spacing: 0.06em;
	color: var(--color-2); background: var(--color-1);
	padding: 0.1rem 0.5rem; border-radius: 6px 0 6px 0;
}
.article-body div[data-secret] > *:last-child { margin-bottom: 0; }

/* Inline secret: a hidden phrase inside a paragraph. Kept subtle so it doesn't
   break the reading flow, but clearly marked (tinted + dashed underline) so an
   editor can see exactly what a reader won't. */
.article-body span[data-secret],
.tiptap-editor__surface span[data-secret] {
	background: color-mix(in srgb, var(--color-1) 12%, transparent);
	border-bottom: 1px dashed var(--color-1);
	border-radius: 2px;
	padding: 0 0.12em;
}
.tiptap-editor__btn--secret { font-size: 0.72rem; font-weight: 600; }

/* The `[[` picker (slice 0040). Positioned against the editor container, which
   is why that container needs to be a positioning context. */
.tiptap-editor { position: relative; }
.page-link-picker {
	position: absolute; z-index: 40; min-width: 16rem; max-width: 26rem;
	max-height: 15rem; overflow-y: auto;
	background: var(--color-2); border: 1px solid var(--color-31);
	border-radius: 4px; box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
}
.page-link-picker__item {
	display: block; width: 100%; text-align: left; cursor: pointer;
	padding: 0.35rem 0.55rem; border: 0; background: none; font: inherit;
}
.page-link-picker__item.is-active, .page-link-picker__item:hover { background: var(--color-21); }
.page-link-picker__title { display: block; color: var(--color-4-darkest); }
/* The path is the point: two pages can share a title, and the path is what the
   link actually stores. */
.page-link-picker__path { display: block; font-size: 0.78rem; color: var(--color-4-lighter); }
.page-link-picker__empty { padding: 0.45rem 0.55rem; font-size: 0.82rem; color: var(--color-4-lighter); }

/* Clicking a link in the editor opens this to reword or retarget it. */
.page-link-dialog {
	position: absolute; z-index: 41; width: 22rem; max-width: calc(100% - 1rem);
	background: var(--color-2); border: 1px solid var(--color-31);
	border-radius: 4px; box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
	padding: 0.6rem; display: flex; flex-direction: column; gap: 0.45rem;
}
.page-link-dialog__row { display: flex; align-items: center; gap: 0.5rem; }
.page-link-dialog__label {
	flex: 0 0 4.2rem; font-size: 0.72rem; text-transform: uppercase;
	letter-spacing: 0.04em; color: var(--color-4-lighter);
}
.page-link-dialog__input {
	flex: 1 1 auto; min-width: 0; font: inherit; font-size: 0.88rem;
	padding: 0.25rem 0.4rem; border: 1px solid var(--color-31);
	border-radius: 3px; background: var(--color-2); color: var(--color-4-darkest);
}
.page-link-dialog__actions { display: flex; justify-content: space-between; gap: 0.5rem; }
.page-link-dialog__btn {
	font: inherit; font-size: 0.82rem; cursor: pointer; padding: 0.25rem 0.6rem;
	border: 1px solid var(--color-31); border-radius: 3px;
	background: none; color: var(--color-4-darkest);
}
.page-link-dialog__btn--primary { background: var(--color-1); border-color: var(--color-1); color: var(--color-2); }
/* The dialog's own suggestion box has to clear it. */
.page-link-dialog ~ .page-link-picker { z-index: 42; }

/* In the editor a link is a chip: it is an atom, so it cannot be typed into —
   showing that is kinder than letting someone try. */
.tiptap-editor__surface span[data-page-link] {
	background: var(--color-21); border-radius: 3px; padding: 0 0.2em;
	box-decoration-break: clone; cursor: pointer;
}
.tiptap-editor__surface span[data-page-link].ProseMirror-selectednode {
	outline: 2px solid var(--color-1); outline-offset: 1px;
}

/* Whitelist / sharing manager (slice 0027 phase 5). */
.grants { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid var(--color-31); }
.grants__title { font-size: 1.1rem; margin: 0 0 0.4rem; color: var(--color-1); }
.grants__hint { font-size: 0.85rem; color: var(--color-4-lighter); margin: 0 0 1rem; max-width: 44rem; }
.grants__list { list-style: none; margin: 0 0 1rem; padding: 0; display: flex; flex-direction: column; gap: 1px; background: var(--color-31); border: 1px solid var(--color-31); border-radius: 6px; overflow: hidden; }
.grants__item { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.55rem 0.8rem; background: var(--color-2); }
.grants__user { font-size: 0.9rem; }
.grants__email { color: var(--color-4-lighter); font-size: 0.8rem; margin-left: 0.4rem; }
.grants__empty { font-size: 0.9rem; color: var(--color-4-lighter); margin: 0 0 1rem; }
.grants__add { display: flex; gap: 0.5rem; max-width: 30rem; }
.grants__input { flex: 1 1 auto; }
.article-body h1 {
    font-size:2rem;
    border-bottom: 2px solid linear-gradient(90deg,rgba(186, 168, 152, 1) 70%, rgba(186, 168, 152, 0) 100%);
}

.article-body h1::after {
    display:block;
    content:"";
    width:70%;
    height:3px;
    background: linear-gradient(90deg,rgba(186, 168, 152, 1) 75%, rgba(186, 168, 152, 0) 100%);
}

.article-body h2 { font-size: 1.5rem; }
.article-body h3 { font-size: 1.2rem;}

/* Infobox — floats right, Wikipedia-style; article text wraps beside it. */
.article-infobox {
	float: right;
	/*width: 300px;*/
	max-width: 100%;
	margin: 0 0 1.25rem 1.5rem;
	background: var(--color-2);
	border: 1px solid var(--color-31);
	border-radius: 8px;
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
	overflow: hidden;
	font-size: 0.9rem;
}
.article-infobox__header {
	position: relative;   /* containing block for the panel's close button */
	background: var(--color-1);
	color: var(--color-2);
	padding: 0.85rem 1rem;
	text-align: center;
}
.article-infobox__title { font-size: 1.15rem; font-weight: 700; line-height: 1.2; }
.article-infobox__subtitle {
	font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em;
	color: var(--color-21); margin-top: 0.2rem;
}
.article-infobox__table { width: 100%; border-collapse: collapse; }
.article-infobox__table th, .article-infobox__table td {
	padding: 0.5rem 0.85rem; text-align: left; vertical-align: top;
	border-bottom: 1px solid var(--color-2-darker);
	overflow-wrap: anywhere;
}
.article-infobox__table th { width: 42%; color: var(--color-4); font-weight: 600; }
.article-infobox__table td { color: var(--color-4-darkest); }
.article-infobox__table tr:last-child th,
.article-infobox__table tr:last-child td { border-bottom: none; }
.article-infobox__table a { color: var(--color-1-lightest); }

@media (max-width: 60rem) {
	.article-infobox { float: none; width: 100%; margin: 0 0 1.25rem; }
	.article-layout { padding: 0 1rem; }
}

/* --- WYSIWYG article edit (mirrors the show layout) --- */
.article-header__title-input {
	width: 100%;
	font-size: 2rem;
	color: var(--color-5);
	line-height: 1.15;
	font-family: inherit;
	font-weight: 700;
	border: none;
	border-bottom: 2px solid transparent;
	background: transparent;
	padding: 0 0 0.15rem;
}
.article-header__title-input:focus {
	outline: none;
	border-bottom-color: var(--color-1);
}
.article-header__title-input::placeholder { color: var(--color-2-darkest); }

.article-body--edit { min-width: 0; }
/* The editor reads like the article body while editing: no form-field panel,
   a faint editable outline that warms on focus, and a taller writing area.
   Body typography already matches the read view — the container carries both
   `article-body` and `article-body--edit`, so `.article-body *` rules apply. */
.article-body--edit .tiptap-editor__surface {
	min-height: 18rem;
	background: transparent;
	border-color: var(--color-31);
}
.article-body--edit .tiptap-editor__surface:focus-within { border-color: var(--color-1); }
/* Keep the formatting controls reachable through a long article — sticking just
   below the persistent topbar (not on top of it) and stacked beneath it. */
.article-body--edit .tiptap-editor__toolbar {
	position: sticky;
	top: var(--topbar-height, 0px);
	z-index: 4;
}

/* Editable infobox: form controls sized to the compact column. */
.article-infobox--edit { width: 100%; }
.article-infobox__table label { font-weight: 600; cursor: pointer; }
.article-infobox__table .infobox-input {
	width: 100%;
	font-size: 0.85rem;
	padding: 0.3rem 0.4rem;
	margin: 0;
}
.article-infobox__table textarea.infobox-input { resize: vertical; }
/* Tom Select copies the <select>'s classes onto .ts-wrapper; the existing
   .ts-wrapper.admin-form__input rule already strips the doubled chrome. */
.article-infobox__table .ts-wrapper.infobox-input { padding: 0; }
.article-infobox__table td .image-field__item { width: 4.5rem; height: 4.5rem; }
.article-infobox__table td .image-field__drop { padding: 0.6rem; font-size: 0.8rem; }

/*---------------------------------------------Login---------------------------------------------*/

.login-wrapper {
	min-width: 340px;
	max-width: 480px;
	margin: auto;
	padding: 2.5rem 3rem;
	background: var(--color-2);
	border-radius: 0.8rem;
	box-shadow: 5px 5px 15px -5px #838383;
	border: 1px solid #dadada;
	border-top: 4px solid var(--color-1);
}

.login-wrapper__title {
	margin: 0 0 1.5rem;
	color: var(--color-1);
	font-size: 1.4rem;
}

.login-form__remember {
	display: flex;
	align-items: center;
	gap: 0.5rem;
}



/*---------------------------------------------Admin Dashboard---------------------------------------------*/
.admin-dashboard {
	width:1500px;
	border:2px solid #dadada;
	margin:1rem auto;
	display:grid;
	align-content: start;
	align-items: baseline;
	padding:3rem;
	grid-template-columns: repeat(1fr, 5);
	background-color: var(--color-2);
	-webkit-box-shadow: 5px 5px 15px -5px #838383; 
	box-shadow: 5px 5px 15px -5px #838383;
	column-gap: 0.5rem;
}

.admin-dashboard__header {
	grid-column-start:  span 5;
	border-bottom:1px solid #dadada;
	font-size: 2rem;
	font-weight: bold;
	
}

.admin-dashboard__description {
	grid-column-start: span 5;
	font-style: italic;
}

.admin-dashboard__options {
	display:grid;
	grid-template-columns: subgrid;
	grid-column-start: span 5;
	margin-top:1rem;
	margin-bottom:1rem;
	
	row-gap: 0.5rem;
}

.admin-dashboard__link {
	padding:5px;
	border-radius:5px;
	height:4rem;
	min-width:200px;
	background:var(--color-2-darker);
	border:2px solid var(--color-2-darkest);
}

/*---------------------------------------------Admin Interface---------------------------------------------*/

.admin-item-wrapper {
	min-width: 50%;
	max-width:1000px;
	margin:1rem auto;
	overflow-y: auto;
	padding:3rem;
	
}

.admin-item-wrapper__item {
	background-color: #dadada;
	height:5rem;
	margin-top:0.5rem;
	width: 100%;
	border-radius: 0.8rem 0.8rem 0 0;
	padding:1rem;
}

.admin-item-wrapper__unstyled-list {
	list-style: none;
	list-style-image: none;
	margin: 0;
    margin-bottom: 0px;
  	padding: 0;
}

.admin-item-wrapper__flexbox {
	display:flex;
	gap:0.5rem;
}

/*---------------------------------------------Admin Card List---------------------------------------------*/

.admin-card-list {
	width: 100%;
	margin: 1.5rem 0;
	padding: 0 2rem;
	overflow-y: auto;
}

.admin-card-list__header {
	display: flex;
	justify-content: space-between;
	align-items: center;
	margin-bottom: 1.25rem;
}

.admin-card-list__title {
	font-size: 1.4rem;
	color: var(--color-1);
	font-weight: bold;
}

/* Section heading within the compendium index (Your worlds / Shared with you). */
.admin-card-list__subhead {
	font-size: 0.78rem;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--color-4-lighter);
	margin: 1.5rem 0 0.5rem;
}
.admin-card-list__subhead:first-of-type { margin-top: 0.5rem; }

.admin-card-list__item {
	display: flex;
	align-items: center;
	gap: 1rem;
	background: var(--color-2);
	border: 1px solid #dadada;
	border-radius: 0.6rem;
	padding: 0.85rem 1.1rem;
	margin-bottom: 0.5rem;
	box-shadow: 0 1px 3px rgba(0,0,0,0.06);
	transition: box-shadow 0.15s ease, border-color 0.15s ease;
}

.admin-card-list__item:hover {
	box-shadow: 0 3px 8px rgba(0,0,0,0.12);
	border-color: var(--color-2-darkest);
}

.admin-card-list__body {
	flex: 1;
	min-width: 0;
}

.admin-card-list__name {
	font-weight: bold;
	color: var(--color-1);
	font-size: 1rem;
}

.admin-card-list__name:hover {
	color: var(--color-1-lighter);
}

.admin-card-list__desc {
	font-size: 0.9rem;
	color: var(--color-4-lighter);
	margin-top: 0.2rem;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.admin-card-list__badge {
	display: inline-block;
	font-size: 0.75rem;
	font-weight: bold;
	padding: 0.2rem 0.55rem;
	border-radius: 999px;
	background: var(--color-2-darker);
	color: var(--color-4);
	white-space: nowrap;
	flex-shrink: 0;
}

.admin-card-list__actions {
	display: flex;
	gap: 0.5rem;
	flex-shrink: 0;
}

.compendium-welcome { margin: 1rem 0 1.5rem; }

/* Article cards, grouped by type on the folder page */
.article-group__heading {
	font-size: 0.95rem;
	color: var(--color-5);
	margin: 1rem 0 0.4rem;
	padding-bottom: 0.25rem;
	border-bottom: 1px solid var(--color-31);
}
.article-cards {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
	gap: 0.75rem;
	margin: 0.5rem 0 1.25rem;
}
.article-card {
	display: flex;
	align-items: center;
	min-height: 3.25rem;
	padding: 0.75rem 0.85rem;
	border: 1px solid var(--color-31);
	border-radius: 6px;
	text-decoration: none;
	color: var(--color-4-darkest);
	background: var(--color-2-lightest, transparent);
	transition: border-color 0.1s ease, box-shadow 0.1s ease, transform 0.1s ease;
}
.article-card:hover {
	border-color: var(--color-1);
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
	transform: translateY(-1px);
}
.article-card__title { font-weight: 600; line-height: 1.25; }

/* Folders */
.compendium-layout {
	display: grid;
	grid-template-columns: 240px minmax(0, 1fr);
	gap: 1.5rem;
	align-items: start;
    width:75%;
}
.compendium-layout__main { min-width: 0; }

.folder-sidebar {
	position: sticky;
	top: 1rem;
	border: 1px solid var(--color-31);
	border-radius: 6px;
	padding: 0.75rem;
	background: var(--color-2-lightest, transparent);
	font-size: 0.9rem;
}
.folder-sidebar__header { margin-bottom: 0.5rem; }
.folder-sidebar__title {
	text-transform: uppercase; letter-spacing: 0.05em; font-size: 0.75rem;
	font-weight: 700; color: var(--color-1); text-decoration: none;
}

.folder-sidebar__footer {
	margin-top: 0.85rem;
	padding-top: 0.6rem;
	border-top: 1px solid var(--color-31);
	display: flex;
	flex-direction: column;
	gap: 0.3rem;
	font-size: 0.8rem;
}
.folder-sidebar__footer a { color: var(--color-1); text-decoration: none; }
.folder-sidebar__footer a:hover { text-decoration: underline; }

.folder-tree, .folder-tree__children { list-style: none; margin: 0; padding: 0; }
.folder-tree__children { padding-left: 0.9rem; border-left: 1px solid var(--color-31); margin-left: 0.35rem; }
.folder-tree__item { margin: 0.1rem 0; }
.folder-tree__summary { cursor: pointer; list-style: none; display: flex; align-items: center; gap: 0.25rem; }
.folder-tree__summary::-webkit-details-marker { display: none; }
.folder-tree__summary::before {
	content: '▸'; font-size: 0.7rem; color: var(--color-4-lighter);
	transition: transform 0.1s ease; display: inline-block;
}
.folder-tree__details[open] > .folder-tree__summary::before { transform: rotate(90deg); }
.folder-tree__link {
	color: var(--color-4-darkest); text-decoration: none; padding: 0.1rem 0.3rem;
	border-radius: 3px; display: inline-block;
}
.folder-tree__link--leaf { margin-left: 0.95rem; }
/* A stub container toggles its branch instead of navigating (slice 0039), so its
   label is plain text. It still fills the row, or the click target would be just
   the glyph — and text selection would fight the toggle on a double click. */
.folder-tree__link--toggle { flex: 1; user-select: none; }
.folder-tree__link:hover { background: var(--color-31); }
.folder-tree__link.is-current, .folder-tree__summary.is-current .folder-tree__link {
	background: var(--color-1); color: #fff; font-weight: 600;
}

@media (max-width: 60rem) {
	.compendium-layout { grid-template-columns: 1fr; }
	.folder-sidebar { position: static; }
}

.folder-breadcrumbs {
	font-size: 0.85rem;
	color: var(--color-4-lighter);
	margin-bottom: 0.75rem;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.35rem;
}
.folder-breadcrumbs a { color: var(--color-1); text-decoration: none; }
.folder-breadcrumbs a:hover { text-decoration: underline; }
.folder-breadcrumbs__sep { color: var(--color-4-lighter); }
.folder-breadcrumbs__current { font-weight: 700; color: var(--color-4-darkest); }

.folder-new {
	display: flex;
	gap: 0.5rem;
	align-items: center;
	margin: 0.5rem 0 1rem;
}
.folder-new__input {
	flex: 1;
	padding: 0.45rem 0.6rem;
	border: 1px solid var(--color-31);
	border-radius: 4px;
	font: inherit;
}

.admin-form__hint { font-size: 0.8rem; color: var(--color-4-lighter); margin-top: 0.35rem; }

/* Calendar config form */
.calendar-units { display: flex; flex-wrap: wrap; gap: 1rem; }
.calendar-units label { display: flex; flex-direction: column; font-size: 0.8rem; font-weight: 600; gap: 0.25rem; }
.calendar-units .admin-form__input { width: 8rem; }
.calendar-rows__list { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 0.5rem; }
.calendar-row {
	display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem;
	padding: 0.4rem; border: 1px solid var(--color-31); border-radius: 5px;
}
.calendar-row__name { flex: 1 1 10rem; padding: 0.35rem 0.5rem; border: 1px solid var(--color-31); border-radius: 4px; font: inherit; }
.calendar-row__num { width: 5rem; padding: 0.35rem 0.5rem; border: 1px solid var(--color-31); border-radius: 4px; font: inherit; }
/* --- Maps (slice 0017) ----------------------------------------------------
   Maps are flat rather than foldered, so the right-hand panel IS the navigation
   between them. */
/* A map wants the screen, unlike the card lists elsewhere in the admin UI —
   so the page opts out of the shared 900px cap rather than inheriting it. */
.map-page {
	display: flex; gap: 1.25rem; align-items: flex-start;
	box-sizing: border-box;
	/* `width: 100%` (a DEFINITE size) so it fills the shell's main column. `width:
	   auto` here collapsed to content width, because in the flex-column
	   `#dashboard__main` the auto horizontal margins below override align-stretch
	   and shrink-wrap the child. Capped + centred for wide screens; the 0 2rem
	   gutter matches the other shell pages. */
	width: 100%; max-width: 1800px; margin: 1.5rem auto; padding: 0 2rem;
}
/* The main column carries `.admin-card-list` too, so this must out-specify it
   (same class count would only win on source order). */
.admin-card-list.map-page__main {
	flex: 1 1 auto; min-width: 0;
	max-width: none; width: auto; margin: 0; padding: 0;
}
.map-list {
	flex: 0 0 14rem; border: 1px solid var(--color-31); border-radius: 4px;
	padding: 0.75rem; background: var(--color-2-lighter);
}
.map-list__header { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin-bottom: 0.6rem; }
.map-list__title { font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; font-size: 0.8rem; }
.map-list__empty { color: var(--color-4-lighter); font-size: 0.85rem; margin: 0; }
.map-list__items { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.3rem; }
.map-list__items a { text-decoration: none; }
.map-list__items li.is-current a { font-weight: 700; text-decoration: underline; }

.map-viewer {
	position: relative; border: 1px solid var(--color-31); border-radius: 4px;
	overflow: hidden; background: var(--color-2-darker); margin-bottom: 1rem;
	/* Leaflet needs a laid-out height before it can size its tile grid; a
	   percentage would collapse because the parent has no fixed height, so this
	   is viewport-relative with a floor for short windows. */
	height: 72vh; min-height: 22rem;
}
/* Marker pins: a div icon, so there is no image asset to resolve (Leaflet's
   default icons break under bundlers) and the colour comes from the shared
   ten-token palette. */
.map-pin {
	display: block; width: 14px; height: 14px; border-radius: 50%;
	background: var(--event-color, var(--event-color-none));
	border: 2px solid var(--color-2); box-shadow: 0 0 0 1px rgba(0,0,0,0.35);
}
.map-pin-wrap { background: none; border: none; }
.map-viewer.is-placing { cursor: crosshair; }
.map-viewer.is-placing .leaflet-container { cursor: crosshair; }

.map-popup__title { font-weight: 700; margin-bottom: 0.25rem; }
.map-popup__link { display: inline-block; }

.map-editor-bar { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.5rem; }
.map-editor-bar__status { font-size: 0.85rem; color: var(--color-4-lighter); }
.map-editor-bar__status.is-error { color: var(--color-1); font-weight: 700; }
[data-action="add-marker"].is-active { outline: 2px solid var(--color-3); }

.map-marker-form { display: flex; flex-direction: column; gap: 0.45rem; min-width: 15rem; }
.map-marker-form__row { display: flex; flex-direction: column; gap: 0.2rem; font-size: 0.8rem; }
.map-marker-form__actions { display: flex; gap: 0.4rem; margin-top: 0.2rem; }

.map-detail-region { cursor: move; }
/* The alignment preview sits under the outline and must never swallow a drag. */
.map-detail-preview { pointer-events: none; }
.map-editor-bar__fade { display: flex; align-items: center; gap: 0.4rem; font-size: 0.8rem; }
.map-editor-bar__fade input { width: 8rem; }
.map-detail-form { display: flex; flex-direction: column; gap: 0.5rem; max-width: 34rem; margin-bottom: 1rem; }
.map-detail-form__row { display: flex; align-items: center; gap: 0.6rem; }
.map-detail-form__row > span:first-child { flex: 0 0 9rem; font-size: 0.85rem; }
.map-detail-form__area { font-size: 0.8rem; color: var(--color-4-lighter); flex: 1; }

/* --- The zoom ladder (slice 0019): rung table + ceiling + layer bands --- */
.map-ladder-ceiling { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.6rem; }
.map-ladder-ceiling label { display: flex; align-items: center; gap: 0.4rem; font-size: 0.85rem; }
.map-ladder-ceiling .admin-form__input { width: 5rem; }
.map-ladder { border-collapse: collapse; margin-bottom: 1rem; font-size: 0.85rem; }
.map-ladder th { padding: 0.2rem 0.6rem; text-align: right; color: var(--color-4-lighter); }
.map-ladder td { padding: 0.2rem 0.6rem; }
.map-ladder__rung { border-left: 3px solid transparent; }
.map-ladder__rung--native { border-left-color: #2f7d46; }
.map-ladder__rung--native .map-ladder__state { color: #2f7d46; }
.map-ladder__rung--upscaled { border-left-color: #c2700a; }
.map-ladder__rung--upscaled .map-ladder__state { color: #c2700a; font-weight: 700; }
.map-ladder__rung--uncovered { border-left-color: var(--color-1); }
.map-ladder__rung--uncovered .map-ladder__state { color: var(--color-1); font-weight: 700; }
.map-ladder__serving { color: var(--color-4-lighter); }

.map-layer-list { list-style: none; margin: 0 0 1rem; padding: 0; }
.map-layer-list__row {
	display: flex;
	align-items: center;
	gap: 0.6rem;
	flex-wrap: wrap;
	padding: 0.35rem 0;
	border-bottom: 1px dashed var(--color-31);
	font-size: 0.85rem;
}
.map-layer-list__kind { flex: 0 0 11rem; color: var(--color-4-lighter); }
.map-layer-list__edit { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.map-layer-list__edit label { display: flex; align-items: center; gap: 0.25rem; font-size: 0.8rem; }
.map-layer-list__label { width: 16rem; }
.map-layer-list__zoom { width: 4rem; }

/* --- "Sharpen this map" (slice 0018): lattice picker + per-cell panel --- */
.map-sharpen-bar__depth { display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; }
.map-sharpen-bar__depth .admin-form__input { width: auto; }
.map-sharpen-cell {
	border: 1px solid var(--color-31);
	border-radius: 4px;
	padding: 0.6rem 0.8rem;
	margin-bottom: 0.75rem;
	max-width: 34rem;
}
.map-sharpen-cell__head { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.3rem; }
.map-sharpen-cell__thumb { width: 3rem; height: 3rem; object-fit: cover; border-radius: 3px; }
.map-sharpen-cell__row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.map-sharpen-cell__warn { color: var(--color-1); font-weight: 700; }
.map-sharpen-cell__check.is-error { color: var(--color-1); font-weight: 700; }
.map-sharpen-queue { list-style: none; margin: 0 0 0.75rem; padding: 0; max-width: 34rem; }
.map-sharpen-queue li {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.6rem;
	padding: 0.3rem 0;
	font-size: 0.85rem;
	border-bottom: 1px dashed var(--color-31);
}
/* A detail layer's tiles: no smoothing when upscaled past their own depth, so
   the map reads as pixel art rather than mush at deep zoom. */
.map-detail-layer { image-rendering: auto; }

/* Geoman's toolbar inherits the app's button look rather than its own. */
.leaflet-pm-toolbar .leaflet-buttons-control-button { border-radius: 3px; }
.map-popup__title { margin-bottom: 0.25rem; }

/* The no-JS fallback: shown until `map-view` removes it. */
.map-viewer__preview { display: block; width: 100%; height: 100%; object-fit: contain; }
/* Leaflet paints its own background; keep the parchment tone behind the tiles. */
.map-viewer .leaflet-container { height: 100%; background: var(--color-2-darker); font: inherit; }

@media (max-width: 60rem) {
	.map-page { flex-direction: column; width: 100%; padding: 0 1rem; }
	.map-list { flex: 1 1 auto; width: 100%; box-sizing: border-box; }
	.map-viewer { height: 60vh; }
}

/* calendar_date field inputs, laid out compactly inside the editable infobox. */
.infobox-date { display: flex; flex-wrap: wrap; gap: 0.3rem; align-items: center; }
.infobox-date__group { display: inline-flex; align-items: center; gap: 0.25rem; }
.infobox-date__legend { font-size: 0.7rem; color: var(--color-4-lighter); }
.infobox-date__num { width: 4.5rem; }
.infobox-date__month { width: 7rem; }
/* A small dot linking a date to the event it put on the timeline. */
.article-infobox__event-link { text-decoration: none; color: var(--color-3); margin-left: 0.35rem; }
.article-infobox__event-link:hover { text-decoration: none; opacity: 0.7; }

/* Era year affixes — short by nature ("AR", "AoK"), so narrower than a name. */
.calendar-row__affix { width: 5rem; padding: 0.35rem 0.5rem; border: 1px solid var(--color-31); border-radius: 4px; font: inherit; }
.calendar-row--leap,
.calendar-row--era { font-size: 0.85rem; }
.calendar-row__inline { display: inline-flex; align-items: center; gap: 0.3rem; }
.calendar-row__check { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.85rem; }
.calendar-row__remove { margin-left: auto; }

/* Current-date controls (calendar show page). */
.calendar-current {
	margin: 1rem 0 1.5rem;
	padding: 1rem;
	border: 1px solid var(--color-31);
	border-radius: 6px;
}
.calendar-current.is-busy { opacity: 0.6; }
.calendar-current__date { font-size: 1.25rem; font-weight: 600; margin-bottom: 0.75rem; }
.calendar-current__steppers { display: flex; flex-wrap: wrap; gap: 0.75rem; }
.calendar-current__unit { display: inline-flex; align-items: center; gap: 0.35rem; }
.calendar-current__step { display: inline; }
.calendar-current__unit-label { min-width: 3.5rem; text-align: center; font-size: 0.85rem; text-transform: capitalize; }
.calendar-current__set { margin-top: 1rem; }
.calendar-current__set-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.5rem; margin-top: 0.5rem; }
.calendar-current__set-form label { display: inline-flex; flex-direction: column; font-size: 0.8rem; gap: 0.2rem; }
.calendar-current__set-form input, .calendar-current__set-form select { width: 6rem; }

/* Month grid (calendar show page). */
.calendar-month { margin: 1rem 0 1.5rem; }
.calendar-month__nav { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.calendar-month__title { font-size: 1.1rem; font-weight: 600; text-align: center; flex: 1; }
.calendar-month__today-link { text-align: center; font-size: 0.85rem; margin: 0.35rem 0 0.75rem; }
.calendar-month__grid { display: grid; grid-template-columns: repeat(var(--cols), 1fr); gap: 4px; }
.calendar-month__strip { display: flex; flex-wrap: wrap; gap: 4px; }
.calendar-month__weekday { text-align: center; font-size: 0.75rem; font-weight: 600; color: var(--color-13, #888); padding: 0.25rem 0; }
.calendar-month__cell {
	min-height: 3.4rem; padding: 0.35rem;
	border: 1px solid var(--color-31); border-radius: 4px;
	display: flex; flex-direction: column; align-items: flex-end;
	font-variant-numeric: tabular-nums;
}
.calendar-month__daynum { line-height: 1; }
/* Markers sit at the bottom-left, so the day number stays where it always was. */
.calendar-month__events {
	display: flex; align-items: center; gap: 2px; flex-wrap: wrap;
	margin-top: auto; align-self: flex-start; max-width: 100%;
}
.event-dot--sm { width: 0.5rem; height: 0.5rem; margin: 0; }
a.event-dot--sm:hover { outline: 2px solid var(--color-3); outline-offset: 1px; }
.calendar-month__more { font-size: 0.65rem; color: var(--color-4-lighter); }

/* Timekeeping landing (slice 0023): shared controls above a two-column
   calendar + upcoming-events layout. */
.timekeeping__controls {
	display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.75rem;
	margin: 0 0 1.25rem;
}
.timekeeping__control { display: inline-flex; flex-direction: column; gap: 0.25rem; font-size: 0.8rem; }
.timekeeping__control-label { color: var(--color-4-lighter); text-transform: uppercase; letter-spacing: 0.03em; font-size: 0.7rem; }
.timekeeping__control select { min-width: 12rem; }

.timekeeping__cols {
	display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
	gap: 1.5rem; align-items: start;
}
@media (max-width: 60rem) { .timekeeping__cols { grid-template-columns: 1fr; } }

.timekeeping__panel {
	border: 1px solid var(--color-31); border-radius: 6px; padding: 1rem;
	min-width: 0;
}
.timekeeping__panel-title { font-size: 1.1rem; color: var(--color-1); margin-bottom: 0.75rem; }

.timekeeping__now { margin-bottom: 0.5rem; }
.timekeeping__now-label { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-4-lighter); }
.timekeeping__now-date { font-size: 1.25rem; font-weight: 600; color: var(--color-1); margin: 0.15rem 0; }
.timekeeping__now-edit { font-size: 0.85rem; }
/* The reused month grid drops its top margin inside the panel. */
.timekeeping__calendar .calendar-month { margin-top: 0.75rem; }

.timekeeping__list { list-style: none; display: flex; flex-direction: column; gap: 0.5rem; }
.timekeeping__event { display: flex; align-items: flex-start; gap: 0.5rem; }
.timekeeping__event-body { min-width: 0; }
.timekeeping__event-name { font-weight: 600; color: var(--color-1); }
.timekeeping__event-name:hover { color: var(--color-1-lighter); }
.timekeeping__event-when { font-size: 0.85rem; color: var(--color-4-lighter); }
.timekeeping__empty { margin: 0; }
.timekeeping__more { margin-top: 1rem; font-size: 0.85rem; }

/* Visually hidden but readable by assistive tech — a bare coloured dot has no
   accessible name otherwise. */
.sr-only {
	position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
	overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.calendar-month__strip .calendar-month__cell { width: 2.75rem; justify-content: center; align-items: center; }
.calendar-month__cell--blank { border: 1px solid transparent; background: transparent; }
.calendar-month__cell.is-today {
	border-color: var(--color-accent, #3b82f6);
	box-shadow: inset 0 0 0 1px var(--color-accent, #3b82f6);
	font-weight: 700;
}

/* --- Timeline (slice 0015, phase 5) ---------------------------------------
   Every positioned element carries its window fraction as `--p` (and era bands
   a width as `--pw`), measured against the window the SERVER rendered. The view
   the user is actually looking at is derived from it by one affine map:

       visible = --p * --tl-k + --tl-b

   `--tl-k` scales (zoom), `--tl-b` offsets (pan); both default to identity, so
   the server-rendered markup is correct with no JS. Because a time axis is
   linear, this map is EXACT — a tick sits where it belongs at any zoom, not
   approximately — which is why the client can zoom freely within a level and
   only refetch when it crosses into the next one. Because it drives `left`
   rather than a `scaleX` transform, text never distorts, and one style write
   repositions everything instead of one write per element. */
.calendar-timeline { margin-bottom: 1.5rem; }
.calendar-timeline__tick,
.calendar-timeline__era,
.calendar-timeline__major-seg,
.calendar-timeline__event,
.calendar-timeline__marker {
	--tl-p: calc(var(--p) * var(--tl-k, 1) + var(--tl-b, 0));
	left: calc(var(--tl-p) * 100%);
}
.calendar-timeline__era,
.calendar-timeline__major-seg,
.calendar-timeline__event--span { width: calc(var(--pw) * var(--tl-k, 1) * 100%); }
/* Dragging must never turn into a text selection. */
.calendar-timeline__viewport { user-select: none; -webkit-user-select: none; }
.calendar-timeline__bar { display: flex; align-items: center; gap: 0.35rem; margin-bottom: 0.5rem; }
.calendar-timeline__spacer { flex: 1; }
.calendar-timeline__scale {
	font-size: 0.8rem; letter-spacing: 0.06em; text-transform: uppercase;
	color: var(--color-4-lighter);
}
.calendar-timeline__viewport {
	position: relative; overflow: hidden;
	border: 1px solid var(--color-31); border-radius: 4px;
	background: var(--color-2-lighter);
	padding: 0.5rem 0 0; cursor: grab; touch-action: pan-y;
}
.calendar-timeline-mount.is-dragging .calendar-timeline__viewport { cursor: grabbing; }
/* The busy dim is delayed, not instant: the ruler re-renders on every gesture
   end, and a fetch that resolves in ~20ms would otherwise read as a flicker.
   Only a genuinely slow request ever becomes visible. */
.calendar-timeline__viewport { transition: opacity 120ms linear 400ms; }
.calendar-timeline-mount.is-busy .calendar-timeline__viewport { opacity: 0.65; }
.calendar-timeline__layer { position: relative; will-change: transform; }

.calendar-timeline__eras { position: relative; height: 1.4rem; margin: 0 0 0.35rem; }
.calendar-timeline__era {
	position: absolute; top: 0; height: 100%;
	background: var(--color-31); opacity: 0.45; border-radius: 3px;
	border-left: 2px solid var(--color-1);
	overflow: hidden;
}
.calendar-timeline__era.is-clipped-start { border-left: 0; border-top-left-radius: 0; border-bottom-left-radius: 0; }
.calendar-timeline__era.is-clipped-end { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.calendar-timeline__era-label {
	/* inline-block, NOT block: the sticky clamp measures offsetWidth to know when
	   the label would overhang its band, and a block fills the band's full width,
	   which reads as "no room to slide" and freezes it in place. */
	display: inline-block; padding: 0 0.4rem; font-size: 0.75rem; line-height: 1.4rem;
	white-space: nowrap; color: var(--color-4-darkest);
}

/* Roomy enough for the tick label + sublabel, and for the event lane (feature 7)
   to land in the space above without another resize.

   The ruler is bottom-aligned: labels sit on the baseline and tick lines rise
   from just above them toward the top, so the empty space belongs to the lanes
   rather than sitting under the text. `--tl-labels` is the reserved label band —
   everything else measures from it, so the two stay in step. */
.calendar-timeline__track { --tl-labels: 2.5rem; position: relative; height: 7.5rem; }
.calendar-timeline__tick { position: absolute; top: 0; height: 100%; }
/* Minor and major lines both run the full height; the major reads as major
   through weight and colour rather than length, so the grid stays legible for
   the lanes above without the ruler turning into a picket fence. */
.calendar-timeline__tick-line {
	position: absolute; bottom: var(--tl-labels); left: 0;
	width: 1px; height: calc(100% - var(--tl-labels));
	background: var(--color-31); opacity: 0.55;
}
.calendar-timeline__tick.is-major .calendar-timeline__tick-line {
	background: var(--color-4-lighter); opacity: 1;
}
.calendar-timeline__tick-label {
	position: absolute; bottom: 1.15rem; left: 0.25rem;
	font-size: 0.75rem; white-space: nowrap; font-variant-numeric: tabular-nums;
	color: var(--color-4-darkest); text-decoration: none;
}
a.calendar-timeline__tick-label:hover { text-decoration: underline; }
.calendar-timeline__tick.is-major .calendar-timeline__tick-label { font-weight: 700; }
.calendar-timeline__tick-sublabel {
	position: absolute; bottom: 0; left: 0.25rem;
	font-size: 0.7rem; white-space: nowrap; color: var(--color-4-lighter);
}

.calendar-timeline__marker { position: absolute; top: 0; height: 100%; z-index: 2; }
.calendar-timeline__marker::before {
	content: ""; position: absolute; top: 0; left: 0; width: 2px; height: 100%;
	background: var(--color-3);
}
.calendar-timeline__marker-label {
	position: absolute; top: 0; left: 0.3rem; white-space: nowrap;
	font-size: 0.7rem; font-weight: 700; color: var(--color-3);
}
/* --- Event lane (slice 0016) ---------------------------------------------
   The lane lives INSIDE the track, so its rows sit on the ruler itself rather
   than over the era band, and they stack UPWARD from just above the tick
   labels — nearest the axis first. Marks carry the same `--p`/`--pw` as the
   ticks, so the ruler's pan/zoom transform moves them at no extra cost. */
.calendar-timeline__events {
	position: absolute; left: 0; right: 0; top: 0; bottom: var(--tl-labels);
	pointer-events: none; z-index: 1;
}
.calendar-timeline__event {
	position: absolute; bottom: calc(var(--row, 0) * 1.25rem);
	display: flex; align-items: center; gap: 0.25rem;
	pointer-events: auto; text-decoration: none; white-space: nowrap;
	line-height: 1;
}
.calendar-timeline__event-dot {
	width: 0.6rem; height: 0.6rem; border-radius: 50%; flex: 0 0 auto;
	background: var(--event-color, var(--event-color-none));
}
.calendar-timeline__event--point { margin-left: -0.3rem; } /* centre the dot on its instant */
.calendar-timeline__event-name {
	font-size: 0.62rem; color: var(--color-4-darkest);
}
.calendar-timeline__event--span {
	height: 0.85rem; border-radius: 3px; overflow: hidden;
	background: var(--event-color, var(--event-color-none));
	min-width: 2px; /* a very short span must still be visible */
}
/* Inside a bar the label sits on the fill, so it needs the contrasting colour.
   `flex: 0 0 auto` keeps it at its true text width: the sticky clamp measures
   offsetWidth to decide whether there is room to slide, and a label that had
   been shrunk to fit would measure as the bar itself and never move. */
.calendar-timeline__event--span .calendar-timeline__event-name {
	flex: 0 0 auto; padding: 0 0.3rem; color: var(--color-2); line-height: 0.85rem;
}
.calendar-timeline__event--cluster {
	min-width: 1.05rem; height: 1.05rem; padding: 0 0.15rem; box-sizing: border-box;
	border-radius: 0.55rem; margin-left: -0.52rem; justify-content: center;
	background: var(--event-color, var(--event-color-none)); color: var(--color-2);
	font-size: 0.62rem; font-weight: 700; line-height: 1.05rem;
}
.calendar-timeline__event--point:hover,
.calendar-timeline__event--span:hover { outline: 2px solid var(--color-3); outline-offset: 1px; }
.calendar-timeline__event--point:hover .calendar-timeline__event-name { text-decoration: underline; }
.calendar-timeline__overflow { margin-top: 0.2rem; }

/* Sticky labels: an era name and a major-unit name sit at their span's start,
   but slide along the viewport edge once that start scrolls off, so whatever
   you are looking at is always named. The offset is applied by the component as
   a transform — CSS `position: sticky` needs a scroll container, and this view
   moves by transform rather than scrolling. */
.calendar-timeline__era-label,
.calendar-timeline__major-label,
.calendar-timeline__event--span .calendar-timeline__event-name { will-change: transform; }

/* Major-unit band along the bottom of the ruler: where the per-tick sublabel
   used to be, but spanning its whole unit so its name can stick. */
.calendar-timeline__majors { position: absolute; left: 0; right: 0; bottom: 0; height: 1rem; }
.calendar-timeline__major-seg { position: absolute; top: -0.25rem; height: 100%; overflow: hidden; }
.calendar-timeline__major-label {
	display: inline-block; padding-left: 0.25rem;
	font-size: 0.7rem; font-weight: 700; white-space: nowrap;
	color: var(--color-4-lighter);
}

/* Sits below the era lane when there is one, so it can't cover an era name.
   (Era lane = viewport padding 0.5 + band 1.4 + gap 0.35.) */
.calendar-timeline.has-eras .calendar-timeline__offscreen { top: 2.4rem; }
.calendar-timeline__offscreen {
	position: absolute; top: 0.5rem; z-index: 3;
	padding: 0.1rem 0.4rem; border-radius: 3px;
	font-size: 0.7rem; font-weight: 700;
	background: var(--color-3); color: var(--color-2);
}
.calendar-timeline__offscreen.is-before { left: 0.25rem; }
.calendar-timeline__offscreen.is-after { right: 0.25rem; }

.admin-card-list__empty {
	color: var(--color-4-lighter);
	padding: 2rem 0;
	text-align: center;
}

.admin-card-list__group {
	background: var(--color-2-darker);
	border: 1px solid #dadada;
	border-radius: 0.75rem;
	padding: 1rem 1.1rem 0.85rem;
	margin-bottom: 1.25rem;
}

.admin-card-list__group-header {
	display: grid;
	grid-template-columns: 1fr auto;
	grid-template-areas:
		"title actions"
		"desc  actions";
	gap: 0.25rem 1rem;
	align-items: center;
	padding-bottom: 0.75rem;
	margin-bottom: 0.75rem;
	border-bottom: 1px solid var(--color-2-darkest, #c8c8c8);
}

.admin-card-list__group-header .admin-card-list__actions {
	grid-area: actions;
	align-self: center;
}

.admin-card-list__group-title {
	grid-area: title;
	display: flex;
	align-items: center;
	gap: 0.5rem;
	font-size: 1.1rem;
	font-weight: bold;
	color: var(--color-1);
}

.admin-card-list__group-title a {
	color: inherit;
}

.admin-card-list__group-title a:hover {
	color: var(--color-1-lighter);
}

.admin-card-list__group-desc {
	grid-area: desc;
	font-size: 0.85rem;
	color: var(--color-4-lighter);
}

.admin-card-list__swatch {
	display: inline-block;
	width: 0.85rem;
	height: 0.85rem;
	border-radius: 50%;
	border: 1px solid rgba(0, 0, 0, 0.2);
	vertical-align: middle;
	margin-right: 0.4rem;
}

/*---------------------------------------------Modal---------------------------------------------*/

.modalSide {
    position: fixed;
	top: 15vh;
	right: 0;
	height: 70vh;
	width: 400px;
	max-width: 90%;
	background: #fff;
	box-shadow: -4px 0 20px rgba(0,0,0,0.2);
	transform: translateX(100%);
	/* Keep the closed modal out of the paint tree until it opens. Without
	   this gate Chrome briefly flashes the panel on initial load before the
	   transform transition settles. Visibility is delayed on close so the
	   slide-out still animates. */
	visibility: hidden;
	transition: transform 0.3s ease, visibility 0s linear 0.3s;
	z-index: 9999;
	display: flex;
	flex-direction: column;
}

.modalSide.open {
  transform: translateX(0);
  visibility: visible;
  transition: transform 0.3s ease, visibility 0s linear 0s;
}

.modal-close-btn {
    all: unset;
    position: absolute;
    top:25px;
    right:25px;
    width:2rem;
    height:2rem;    
    background-image: url('data:image/svg+xml,<svg fill="%23c5c5c5" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" stroke="%23c5c5c5" stroke-width="0.00016"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M0 14.545L1.455 16 8 9.455 14.545 16 16 14.545 9.455 8 16 1.455 14.545 0 8 6.545 1.455 0 0 1.455 6.545 8z" fill-rule="evenodd"></path></g></svg>');
    background-repeat: no-repeat;
    background-position: center;
    cursor: pointer;
}

.modal-close-btn:active {
    background-image: url('data:image/svg+xml,<svg fill="%23a5a5a5" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" stroke="%23a5a5a5" stroke-width="0.00016"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M0 14.545L1.455 16 8 9.455 14.545 16 16 14.545 9.455 8 16 1.455 14.545 0 8 6.545 1.455 0 0 1.455 6.545 8z" fill-rule="evenodd"></path></g></svg>');
    transform: translateX(2px) translateY(-2px);
}

/* Lock toggle (TODO/0 #21): sits left of the close button. The padlock SVGs are
   white-filled, so they're used as masks and tinted via background-color. */
.modal-lock-btn {
    all: unset;
    position: absolute;
    top: 27px;
    right: 64px;
    width: 1.5rem;
    height: 1.5rem;
    cursor: pointer;
    background-color: #c5c5c5;
    -webkit-mask: url('/images/icons/icon-padlock-open.svg') center / contain no-repeat;
    mask: url('/images/icons/icon-padlock-open.svg') center / contain no-repeat;
}

.modal-lock-btn[hidden] { display: none; }

.modal-lock-btn:hover { background-color: #888; }

.modal-lock-btn--locked {
    background-color: #6a5d44;
    -webkit-mask-image: url('/images/icons/icon-padlock.svg');
    mask-image: url('/images/icons/icon-padlock.svg');
}

.modal-back-btn {
    all: unset;
    position: absolute;
    top: 25px;
    left: 25px;
    max-width: calc(100% - 80px);
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 8px 4px 24px;
    font-size: 0.85rem;
    color: #888;
    cursor: pointer;
    background-image: url('data:image/svg+xml,<svg fill="%23c5c5c5" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z" fill-rule="evenodd"/></svg>');
    background-repeat: no-repeat;
    background-position: left center;
    background-size: 16px 16px;
}

.modal-back-btn[hidden] {
    display: none;
}

.modal-back-btn:hover {
    color: #555;
}

.modal-back-btn:active {
    transform: translateX(-2px);
}

.modal-back-btn__label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.modal-content {
  padding: 32px 16px 16px;
  overflow-y: auto;
  flex: 1;
}

.spinner {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
}

.book,
.book__pg-shadow,
.book__pg {
	animation: cover 7s ease-in-out infinite;
}
.book {
	background-color: var(--color-1);
	border-radius: 0.25em;
	box-shadow:
		0 0.25em 0.5em hsla(0,0%,0%,0.3),
		0 0 0 0.25em var(--color-1-darker) inset;
	padding: 0.25em;
	perspective: 37.5em;
	position: relative;
	width: 8em;
	height: 6em;
	transform: translate3d(0,0,0);
	transform-style: preserve-3d;
}
.book__pg-shadow,
.book__pg {
	position: absolute;
	left: 0.25em;
	width: calc(50% - 0.25em);
}
.book__pg-shadow {
	animation-name: shadow;
	background-image: linear-gradient(-45deg,hsla(0,0%,0%,0) 50%,hsla(0,0%,0%,0.3) 50%);
	filter: blur(0.25em);
	top: calc(100% - 0.25em);
	height: 3.75em;
	transform: scaleY(0);
	transform-origin: 100% 0%;
}
.book__pg {
	animation-name: pg1;
	background-color: var(--color-2);
	background-image: linear-gradient(90deg,hsla(223,10%,90%,0) 87.5%,hsl(223,10%,90%));
	height: calc(100% - 0.5em);
	transform-origin: 100% 50%;
}

.book__pg {
    background-image:
		repeating-linear-gradient(hsl(223,10%,10%) 0 0.125em,hsla(223,10%,10%,0) 0.125em 0.5em),
		linear-gradient(90deg,hsla(223,10%,90%,0) 87.5%,hsl(223,10%,90%));
    background-repeat: no-repeat;
	background-position: center;
	background-size: 2.5em 4.125em, 100% 100%;
}
.book__pg--2,
.book__pg--3,
.book__pg--4 {
	background-image:
		repeating-linear-gradient(hsl(223,10%,10%) 0 0.125em,hsla(223,10%,10%,0) 0.125em 0.5em),
		linear-gradient(90deg,hsla(223,10%,90%,0) 87.5%,hsl(223,10%,90%));
	background-repeat: no-repeat;
	background-position: center;
	background-size: 2.5em 4.125em, 100% 100%;
}
.book__pg--2 {
	animation-name: pg2;
}
.book__pg--3 {
	animation-name: pg3;
}
.book__pg--4 {
	animation-name: pg4;
}
.book__pg--5 {
	animation-name: pg5;
}

/* Animations */
@keyframes cover {
	from, 5%, 45%, 55%, 95%, to {
		animation-timing-function: ease-out;
		background-color: var(--color-1);
	}
	10%, 40%, 60%, 90% {
		animation-timing-function: ease-in;
		background-color: var(--color-1-darker);
	}
}
@keyframes shadow {
	from, 10.01%, 20.01%, 30.01%, 40.01% {
		animation-timing-function: ease-in;
		transform: translate3d(0,0,1px) scaleY(0) rotateY(0);
	}
	5%, 15%, 25%, 35%, 45%, 55%, 65%, 75%, 85%, 95% {
		animation-timing-function: ease-out;
		transform: translate3d(0,0,1px) scaleY(0.2) rotateY(90deg);
	}
	10%, 20%, 30%, 40%, 50%, to {
		animation-timing-function: ease-out;
		transform: translate3d(0,0,1px) scaleY(0) rotateY(180deg);
	}
	50.01%, 60.01%, 70.01%, 80.01%, 90.01% {
		animation-timing-function: ease-in;
		transform: translate3d(0,0,1px) scaleY(0) rotateY(180deg);
	}
	60%, 70%, 80%, 90%, to {
		animation-timing-function: ease-out;
		transform: translate3d(0,0,1px) scaleY(0) rotateY(0);
	}
}
@keyframes pg1 {
	from, to {
		animation-timing-function: ease-in-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(0.4deg);
	}
	10%, 15% {
		animation-timing-function: ease-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(180deg);
	}
	20%, 80% {
		animation-timing-function: ease-in;
		background-color: var(--color-2-darker);
		transform: translate3d(0,0,1px) rotateY(180deg);
	}
	85%, 90% {
		animation-timing-function: ease-in-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(180deg);
	}
}
@keyframes pg2 {
	from, to {
		animation-timing-function: ease-in;
		background-color: var(--color-2-darker);
		transform: translate3d(0,0,1px) rotateY(0.3deg);
	}
	5%, 10% {
		animation-timing-function: ease-in-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(0.3deg);
	}
	20%, 25% {
		animation-timing-function: ease-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(179.9deg);
	}
	30%, 70% {
		animation-timing-function: ease-in;
		background-color: var(--color-2-darker);
		transform: translate3d(0,0,1px) rotateY(179.9deg);
	}
	75%, 80% {
		animation-timing-function: ease-in-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(179.9deg);
	}
	90%, 95% {
		animation-timing-function: ease-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(0.3deg);
	}
}
@keyframes pg3 {
	from, 10%, 90%, to {
		animation-timing-function: ease-in;
		background-color: var(--color-2-darker);
		transform: translate3d(0,0,1px) rotateY(0.2deg);
	}
	15%, 20% {
		animation-timing-function: ease-in-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(0.2deg);
	}
	30%, 35% {
		animation-timing-function: ease-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(179.8deg);
	}
	40%, 60% {
		animation-timing-function: ease-in;
		background-color: var(--color-2-darker);
		transform: translate3d(0,0,1px) rotateY(179.8deg);
	}
	65%, 70% {
		animation-timing-function: ease-in-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(179.8deg);
	}
	80%, 85% {
		animation-timing-function: ease-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(0.2deg);
	}
}
@keyframes pg4 {
	from, 20%, 80%, to {
		animation-timing-function: ease-in;
		background-color: var(--color-2-darker);
		transform: translate3d(0,0,1px) rotateY(0.1deg);
	}
	25%, 30% {
		animation-timing-function: ease-in-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(0.1deg);
	}
	40%, 45% {
		animation-timing-function: ease-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(179.7deg);
	}
	50% {
		animation-timing-function: ease-in;
		background-color: var(--color-2-darker);
		transform: translate3d(0,0,1px) rotateY(179.7deg);
	}
	55%, 60% {
		animation-timing-function: ease-in-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(179.7deg);
	}
	70%, 75% {
		animation-timing-function: ease-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(0.1deg);
	}
}
@keyframes pg5 {
	from, 30%, 70%, to {
		animation-timing-function: ease-in;
		background-color: var(--color-2-darker);
		transform: translate3d(0,0,1px) rotateY(0);
	}
	35%, 40% {
		animation-timing-function: ease-in-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(0deg);
	}
	50% {
		animation-timing-function: ease-in-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(179.6deg);
	}
	60%, 65% {
		animation-timing-function: ease-out;
		background-color: var(--color-2);
		transform: translate3d(0,0,1px) rotateY(0);
	}
}

dialog:focus { outline: 1px solid var(--color-1)}

dialog[ open ] {
			animation-duration: 200ms ;
			animation-fill-mode: forwards ;
			animation-iteration-count: 1 ;
			animation-name: dialogEnter ;
			animation-timing-function: ease-out ;
		}

@keyframes dialogEnter {
    from {
        opacity: 0.7 ;
        transform: translateX(150%);
    }
    to {
        opacity: 1.0 ;
        transform: translateX(5%);
    }
}

/*---------------------------------------------Admin Form---------------------------------------------*/

/* A focused form / management card — centered and constrained (its designed
   look). The full-width layout (item 4) is carried by the content wrappers
   (.article-layout, .admin-card-list) and the dashboard grid, not this card;
   forms and card pages read better centered than stretched edge-to-edge. */
.admin-create-wrapper {
    width: 100%;
    max-width: 820px;
    margin: 1.5rem auto;
    padding: 2.5rem 3rem;
    background: var(--color-2);
    border-radius: 0.8rem;
    box-shadow: 5px 5px 15px -5px #838383;
    border: 1px solid #dadada;
    overflow-y: auto;
}

.admin-create-wrapper__title {
    font-size: 1.5rem;
    font-weight: bold;
    color: var(--color-1);
    margin-bottom: 1.5rem;
    padding-bottom: 0.75rem;
    border-bottom: 2px solid var(--color-1-lighter);
}

.admin-form__group {
    margin-bottom: 1.25rem;
}

.admin-form__label {
    display: block;
    font-size: 0.8rem;
    font-weight: bold;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-4);
    margin-bottom: 0.4rem;
}

.admin-form__input,
.admin-form__textarea,
.tiptap-editor__surface {
    display: block;
    width: 100%;
    padding: 0.6rem 0.8rem;
    font-size: 1rem;
    font-family: inherit;
    color: var(--color-4-darkest);
    background: #fff;
    border: 1.5px solid var(--color-2-darkest);
    border-radius: 0.4rem;
    box-sizing: border-box;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
    outline: none;
}

.admin-form__input:focus,
.admin-form__textarea:focus {
    border-color: var(--color-1);
    box-shadow: 0 0 0 3px rgba(78, 25, 25, 0.12);
}

.admin-form__textarea {
    resize: vertical;
    min-height: 120px;
}

.admin-form__table {
    width: 100%;
}

.admin-form__table th {
    text-align: left;
}

.admin-form__actions {
    display: flex;
    gap: 0.75rem;
    align-items: center;
    margin-top: 2rem;
    flex-wrap: wrap;
}

.btn {
    display: inline-block;
    padding: 0.6rem 1.4rem;
    font-size: 0.9rem;
    font-family: inherit;
    font-weight: bold;
    border-radius: 0.4rem;
    border: 1.5px solid transparent;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
    text-decoration: none;
    line-height: 1.4;
}

.btn:active {
    transform: scale(0.97);
}

.btn--primary {
    background: var(--color-1);
    color: #fafafa;
    border-color: var(--color-1-darkest);
}

.btn--primary:hover {
    background: var(--color-1-darkest);
    border-color: var(--color-1-darkest);
}

.btn--secondary {
    background: transparent;
    color: var(--color-1);
    border-color: var(--color-1-lighter);
}

.btn--secondary:hover {
    background: rgba(78, 25, 25, 0.06);
    border-color: var(--color-1);
}

/* ---------------------------------------------------------------------------
   Article Type field list (slice 0036) — one <details> per field.

   Replaces the old table: a <details> cannot wrap <tr>s, so the rows are a
   grid instead. The header strip and every summary share ONE column template
   (`--field-cols`) so they stay aligned, which is the only thing the table was
   still buying.
   --------------------------------------------------------------------------- */

.field-table {
    --field-cols: 2.6rem minmax(7rem, 1.5fr) minmax(4rem, .7fr) minmax(7rem, 1.1fr) auto;
}

.field-list__head,
.field-row__summary {
    display: grid;
    grid-template-columns: var(--field-cols);
    gap: 0.6rem;
    align-items: center;
}

.field-list__head {
    padding: 0 0.6rem 0.35rem;
    font-size: 0.7rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-4-lighter);
}

.field-list {
    border: 1.5px solid var(--color-2-darkest);
    border-radius: 0.4rem;
    background: #fff;
    overflow: hidden;
}

.field-row + .field-row { border-top: 1px solid var(--color-2-darker); }
.field-row[open] { background: var(--color-2); }

.field-row__summary {
    padding: 0.5rem 0.6rem;
    cursor: pointer;
    /* `display: grid` already removes the marker in modern engines; this is for
       the WebKit-prefixed one, which ignores that. */
    list-style: none;
}
.field-row__summary::-webkit-details-marker { display: none; }
.field-row__summary:hover { background: var(--color-2); }
.field-row[open] > .field-row__summary { border-bottom: 1px solid var(--color-2-darker); }

.field-row__grip { display: flex; align-items: center; gap: 0.15rem; }
.field-row__chev {
    color: var(--color-4-lighter);
    font-size: 0.8rem;
    transition: transform 0.12s ease;
}
.field-row[open] .field-row__chev { transform: rotate(90deg); }
.field-row__drag { color: var(--color-4-lighter); cursor: grab; }

.field-row__label { font-weight: 700; overflow-wrap: anywhere; }
.field-row__key { font-size: 0.8rem; color: var(--color-4-lighter); overflow-wrap: anywhere; }
.field-row__type { font-size: 0.85rem; }
.field-row__type small { display: block; color: var(--color-4-lighter); }

.field-row__badges { display: flex; flex-wrap: wrap; gap: 0.3rem; justify-content: flex-end; }
.field-badge {
    padding: 0.1rem 0.4rem;
    border-radius: 999px;
    border: 1px solid var(--color-2-darkest);
    background: #fff;
    font-size: 0.7rem;
    white-space: nowrap;
    color: var(--color-4-lighter);
}
.field-badge--secret { border-color: var(--color-3); color: var(--color-3); }

.field-row__panel { padding: 0.75rem 0.9rem 1rem; }
.field-row__panel .admin-form__help { font-size: 0.78rem; }

.field-row__group {
    margin: 0.75rem 0 0;
    padding: 0.5rem 0.7rem 0.7rem;
    border: 1px solid var(--color-2-darkest);
    border-radius: 0.35rem;
    background: #fff;
}
.field-row__group > legend {
    padding: 0 0.35rem;
    font-size: 0.72rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-4-lighter);
}
.field-row__group--locked { background: var(--color-2); }

.field-row__subform {
    margin-top: 1rem;
    padding-top: 0.9rem;
    border-top: 1px dashed var(--color-2-darkest);
}

/* Reorder ▲/▼ and Remove. The `hidden` attribute must win over the flex
   display, so boundary forms are excluded rather than re-hidden. */
.field-row__tools {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 1rem;
    padding-top: 0.75rem;
    border-top: 1px dashed var(--color-2-darkest);
}
.field-row__tools form[hidden] { display: none; }

@media (max-width: 60rem) {
    .field-table { --field-cols: 2.6rem 1fr auto; }
    .field-list__head > :nth-child(3),
    .field-list__head > :nth-child(4),
    .field-row__key,
    .field-row__type { display: none; }
}

/* Compact square button for row actions (e.g. field reorder ▲/▼). */
.btn--icon {
    padding: 0.3rem 0.5rem;
    font-size: 0.75rem;
    font-weight: normal;
    background: transparent;
    color: var(--color-1);
    border-color: var(--color-1-lighter);
    line-height: 1;
}
.btn--icon:hover {
    background: rgba(78, 25, 25, 0.06);
    border-color: var(--color-1);
}

.admin-form__error-list {
    list-style: none;
    padding: 0;
    margin-bottom: 1.5rem;
    border-left: 3px solid #c0392b;
    background: rgba(192, 57, 43, 0.06);
    border-radius: 0 0.4rem 0.4rem 0;
}

.admin-form__error-list li {
    padding: 0.5rem 1rem;
    color: #c0392b;
    font-size: 0.9rem;
}

.admin-form__success {
    padding: 0.7rem 1rem;
    margin-bottom: 1.5rem;
    background: rgba(39, 174, 96, 0.1);
    border-left: 3px solid #27ae60;
    border-radius: 0 0.4rem 0.4rem 0;
    color: #1e8449;
    font-size: 0.9rem;
    font-weight: bold;
}

.admin-create-wrapper__back {
    display: inline-block;
    margin-bottom: 1.5rem;
    color: var(--color-4);
    font-size: 0.85rem;
    font-weight: bold;
    transition: color 0.15s ease;
}

.admin-create-wrapper__back:hover {
    color: var(--color-1);
}

.admin-form__help {
    margin-top: 0.4rem;
    font-size: 0.8rem;
    color: var(--color-4-lighter);
    font-style: italic;
}

.admin-form__autosave-status {
    margin-left: 0.6rem;
    font-size: 0.8rem;
    font-style: italic;
    color: var(--color-4-lighter);
}

.admin-create-wrapper--wide {
    max-width: 1100px;
}

.admin-form__section-title {
    font-size: 1.05rem;
    font-weight: bold;
    color: var(--color-1);
    margin: 2rem 0 1rem;
    padding-bottom: 0.4rem;
    border-bottom: 1px solid var(--color-1-lighter);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.admin-form__section-title:first-of-type {
    margin-top: 0;
}

.admin-form__row {
    display: flex;
    gap: 1rem;
    flex-wrap: wrap;
    align-items: flex-start;
}

.admin-form__row > .admin-form__group {
    flex: 0 1 auto;
    min-width: 9rem;
}

.admin-form__row > .admin-form__group--grow {
    flex: 1 1 14rem;
}

.admin-form__checkbox {
    display: flex;
    align-items: center;
}

.admin-form__checkbox label {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.9rem;
    color: var(--color-4);
    text-transform: none;
    letter-spacing: normal;
    font-weight: normal;
    cursor: pointer;
}

.admin-form__checkbox input[type="checkbox"] {
    accent-color: var(--color-1);
    width: 1rem;
    height: 1rem;
}

.admin-form__details {
    margin-bottom: 1.25rem;
    border: 1px dashed var(--color-2-darkest);
    border-radius: 0.4rem;
    padding: 0.4rem 0.9rem;
    background: rgba(0, 0, 0, 0.02);
}

.admin-form__details > summary {
    cursor: pointer;
    font-size: 0.8rem;
    font-weight: bold;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-4);
    padding: 0.4rem 0;
    user-select: none;
}

.admin-form__details[open] > summary {
    margin-bottom: 0.6rem;
    border-bottom: 1px dashed var(--color-2-darkest);
}

.btn--danger {
    background: transparent;
    color: #c0392b;
    border-color: #c0392b;
}

.btn--danger:hover {
    background: rgba(192, 57, 43, 0.08);
}

/* ---------------------------------------------------------------------
   Admin form: sub-sections & linked lists
   --------------------------------------------------------------------- */

.admin-form__sub-section {
    margin-top: 2rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--color-2-darkest);
}

.admin-form__sub-section-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.admin-form__sub-section-header .admin-form__section-title {
    margin: 0;
    border-bottom: none;
    padding-bottom: 0;
}

.admin-form__linked-list {
    list-style: none;
    padding: 0;
    margin: 0.5rem 0 0;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.admin-form__linked-list li {
    padding: 0.5rem 0.8rem;
    background: rgba(0, 0, 0, 0.03);
    border: 1px solid var(--color-2-darkest);
    border-radius: 0.3rem;
}

.admin-form__linked-list li a {
    color: var(--color-1);
    text-decoration: none;
    font-weight: 500;
}

.admin-form__linked-list li a:hover {
    text-decoration: underline;
}

.admin-form__empty {
    margin: 0.5rem 0 0;
    color: var(--color-4-lighter);
    font-style: italic;
    font-size: 0.9rem;
}

/* ---------------------------------------------------------------------
   Culture form: skill & passion entry rows
   --------------------------------------------------------------------- */

.culture-form-skill-list,
.culture-form-passion-list {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.culture-skill-entry,
.culture-passion-entry {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.6rem;
    background: rgba(0, 0, 0, 0.03);
    border: 1px solid var(--color-2-darkest);
    border-radius: 0.3rem;
}

.culture-skill-entry__name {
    flex: 1;
    font-size: 0.95rem;
    color: var(--color-4);
}

/* #45b: per-offering specialisation field on a culture skill (blank = "(Any)"). */
.culture-skill-spec {
    flex: 0 0 9rem;
    width: 9rem;
    padding: 0.15rem 0.4rem;
    border: 1px solid var(--color-2-darkest);
    border-radius: 0.2rem;
}

.culture-passion-entry .passion-input {
    flex: 1;
    margin: 0;
}

.culture-skill-add-row,
.culture-passion-add-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.3rem;
}

.culture-skill-add-row select,
.culture-passion-add-row input {
    flex: 1;
    margin: 0;
}

/* ---------------------------------------------------------------------
   Career form
   --------------------------------------------------------------------- */

/* Characteristic prereq rows ------------------------------------------ */
.career-form__char-list {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.career-form__char-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.career-form__char-row select {
    flex: 0 0 12rem;
    margin: 0;
}

.career-form__char-row input[type="number"] {
    flex: 0 0 6rem;
    margin: 0;
}

/* Rank blocks (used by both prereq and ability sections) -------------- */
.career-form__rank-block {
    margin-top: 1rem;
    padding: 0.8rem 1rem;
    background: rgba(0, 0, 0, 0.02);
    border: 1px solid var(--color-2-darkest);
    border-radius: 0.4rem;
}

.career-form__rank-title {
    margin: 0 0 0.6rem;
    font-size: 0.95rem;
    font-weight: bold;
    color: var(--color-1);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.career-form__rank-meta {
    font-weight: normal;
    text-transform: none;
    letter-spacing: normal;
    color: var(--color-4-lighter);
    font-style: italic;
    font-size: 0.85rem;
    margin-left: 0.4rem;
}

/* Skill chip (groupable item with prereq star) ------------------------ */
.career-form__skill-area {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0.4rem;
    padding: 0.6rem;
    min-height: 3rem;
    border: 1px dashed var(--color-2-darkest);
    border-radius: 0.3rem;
    background: rgba(255, 255, 255, 0.4);
}

.career-form__chip {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.2rem 0.5rem 0.2rem 0.3rem;
    background: var(--color-2-lightest, #eef);
    border: 1px solid var(--color-2-darkest);
    border-radius: 999px;
    font-size: 0.85rem;
    color: var(--color-4);
    cursor: grab;
    user-select: none;
}

.career-form__chip:active { cursor: grabbing; }

.career-form__chip.is-dragging {
    opacity: 0.4;
}

.career-form__chip.is-prereq {
    background: rgba(220, 170, 30, 0.14);
    border-color: rgba(180, 130, 0, 0.55);
}

.career-form__chip-name {
    line-height: 1;
}

/* #45b: per-offering specialisation field on a skill chip. Blank = "(Any)". */
.career-form__chip-spec {
    width: 8rem;
    margin: 0 0.25rem;
    padding: 0.05rem 0.3rem;
    font-size: 0.8rem;
    border: 1px solid #d8d2c5;
    border-radius: 0.2rem;
}

.career-form__chip-star {
    border: none;
    background: transparent;
    color: var(--color-4-lighter);
    font-size: 0.95rem;
    line-height: 1;
    padding: 0 0.1rem;
    cursor: pointer;
}

.career-form__chip.is-prereq .career-form__chip-star {
    color: rgba(180, 130, 0, 0.95);
}

.career-form__chip-star:hover { color: rgba(180, 130, 0, 0.95); }

.career-form__chip-remove {
    border: none;
    background: transparent;
    color: var(--color-4-lighter);
    font-size: 1rem;
    line-height: 1;
    padding: 0 0.1rem;
    cursor: pointer;
}

.career-form__chip-remove:hover { color: #c0392b; }

.career-form__skill-add {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 14rem;
}

.career-form__skill-add select {
    margin: 0;
    flex: 1;
}

/* Rank skill counts panel -------------------------------------------- */
.career-form__rank-counts {
    display: flex;
    flex-wrap: wrap;
    gap: 0.8rem;
    padding: 0.6rem;
    border: 1px dashed var(--color-2-darkest);
    border-radius: 0.3rem;
    background: rgba(255, 255, 255, 0.4);
}

.career-form__rank-count-cell {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    flex: 0 0 7rem;
}

.career-form__rank-count-cell .admin-form__label {
    margin: 0;
}

.career-form__rank-count-cell input[type="number"] {
    margin: 0;
}

/* OR cluster (skills + prereqs) --------------------------------------- */
.or-cluster {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.35rem;
    padding: 0.3rem 0.5rem;
    background: rgba(255, 200, 60, 0.10);
    border: 1px dashed rgba(180, 140, 0, 0.5);
    border-radius: 0.4rem;
    position: relative;
}

.or-cluster::before {
    content: 'OR';
    position: absolute;
    top: -0.55rem;
    left: 0.5rem;
    background: var(--color-bg, #fff);
    padding: 0 0.3rem;
    font-size: 0.65rem;
    font-weight: bold;
    color: rgba(140, 100, 0, 0.85);
    letter-spacing: 0.08em;
}

/* Ability area + nodes ------------------------------------------------ */
.career-form__ability-area {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    padding: 0.6rem;
    min-height: 3rem;
    border: 1px dashed var(--color-2-darkest);
    border-radius: 0.3rem;
    background: rgba(255, 255, 255, 0.4);
}

.career-form__ability-node {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.45rem 0.7rem;
    background: #fff;
    border: 1px solid var(--color-2-darkest);
    border-radius: 0.3rem;
    cursor: grab;
    user-select: none;
}

.career-form__ability-node:active { cursor: grabbing; }

.career-form__ability-node.is-dragging { opacity: 0.4; }

.ability-node__name {
    color: var(--color-1);
    text-decoration: none;
    font-weight: 500;
}

.ability-node__name:hover { text-decoration: underline; }

.ability-node__remove {
    border: none;
    background: transparent;
    color: var(--color-4-lighter);
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
}

.ability-node__remove:hover { color: #c0392b; }

/* XOR cluster (abilities) --------------------------------------------- */
.xor-cluster {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    padding: 0.5rem 0.6rem 0.6rem;
    background: rgba(60, 120, 200, 0.06);
    border: 1px dashed rgba(40, 90, 160, 0.5);
    border-radius: 0.4rem;
    position: relative;
}

.xor-cluster::before {
    content: 'CHOOSE ONE';
    position: absolute;
    top: -0.55rem;
    left: 0.6rem;
    background: var(--color-bg, #fff);
    padding: 0 0.3rem;
    font-size: 0.65rem;
    font-weight: bold;
    color: rgba(40, 90, 160, 0.9);
    letter-spacing: 0.08em;
}

.xor-cluster__label {
    margin: 0;
    padding: 0.25rem 0.5rem;
    font-size: 0.85rem;
    font-style: italic;
    border: 1px solid transparent;
    background: transparent;
    color: var(--color-4);
}

.xor-cluster__label:focus {
    background: #fff;
    border-color: var(--color-2-darkest);
    outline: none;
    font-style: normal;
}

.xor-cluster__members {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

/* Orphan (unplaced) section — only visible when it contains something --- */
.career-form__orphan-block {
    border-style: dashed;
    background: rgba(180, 90, 30, 0.05);
}

.career-form__orphan-block .career-form__rank-title {
    color: rgba(140, 70, 0, 0.95);
}

.career-form__orphan-block:not(:has([data-ability-id])) {
    display: none;
}

/* × means "remove placement from this rank" — meaningless inside the orphan section. */
.career-form__orphan-block .ability-node__remove {
    display: none;
}

/* Per-rank controls (+ New Ability / + Place Existing) ---------------- */
.career-form__ability-controls {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 0.5rem;
    flex-wrap: wrap;
}

.career-form__place-existing {
    flex: 1;
    min-width: 16rem;
}

.career-form__place-existing select {
    margin: 0;
}



/* ---------------------------------------------------------------------
   Custom Text Parser ("ct-")
   Styles for parsed descriptions: headers, emphasis, compendium links.
   --------------------------------------------------------------------- */

.ct-text {
    line-height: 1.5;
    color: var(--color-4);
}

.ct-text p {
    margin: 0 0 0.75rem 0;
}

.ct-text p:last-child {
    margin-bottom: 0;
}

.ct-text strong {
    font-weight: 700;
    color: var(--color-4-darker);
}

.ct-text em {
    font-style: italic;
}

.ct-text ul {
    list-style: disc outside;
    padding-left: 1.5rem;
    margin: 0 0 0.75rem 0;
}

.ct-text ul li {
    margin: 0.15rem 0;
}

.ct-text ul li > p {
    margin: 0;
}

.ct-text ol {
    list-style: decimal outside;
    padding-left: 1.5rem;
    margin: 0 0 0.75rem 0;
}

.ct-text ol li {
    margin: 0.15rem 0;
}

.ct-text ol li > p {
    margin: 0;
}

.ct-text blockquote {
    margin: 0 0 0.75rem 0;
    padding: 0.4rem 0.9rem;
    border-left: 3px solid var(--color-1);
    background: var(--color-2-darker);
    color: var(--color-4);
    font-style: italic;
}

.ct-text blockquote p:last-child {
    margin-bottom: 0;
}

.ct-text hr {
    border: 0;
    border-top: 1px solid var(--color-31);
    margin: 1rem 0;
}

.ct-text table {
    border-collapse: collapse;
    margin: 0 0 0.75rem 0;
    width: 100%;
    table-layout: fixed;
    overflow: hidden;
}

.ct-text table td,
.ct-text table th {
    border: 1px solid var(--color-2-darkest);
    padding: 0.35rem 0.5rem;
    vertical-align: top;
    position: relative;
}

.ct-text table th {
    background: var(--color-2-darker);
    color: var(--color-4-darker);
    font-weight: 700;
    text-align: left;
}

.ct-text table p {
    margin: 0;
}

/* TipTap table editing affordances */
.tiptap-editor__surface .ProseMirror .selectedCell {
    background: rgba(0, 0, 0, 0.08);
}

.tiptap-editor__surface .ProseMirror .column-resize-handle {
    position: absolute;
    right: -2px;
    top: 0;
    bottom: -2px;
    width: 4px;
    background: var(--color-1);
    pointer-events: none;
}

.tiptap-editor__surface .ProseMirror.resize-cursor {
    cursor: col-resize;
}

.ct-h1, .ct-text h1,
.ct-h2, .ct-text h2,
.ct-h3, .ct-text h3 {
    color: var(--color-1);
    font-family: Georgia, 'Times New Roman', serif;
    font-weight: 700;
    line-height: 1.25;
    margin: 1rem 0 0.5rem 0;
    border-bottom: 1px solid var(--color-31);
    padding-bottom: 0.2rem;
}

.ct-h1, .ct-text h1 {
    font-size: 1.4rem;
    border-bottom-width: 2px;
}

.ct-h2, .ct-text h2 {
    font-size: 1.2rem;
}

.ct-h3, .ct-text h3 {
    font-size: 1.05rem;
    border-bottom-style: dotted;
}

.ct-text .ct-h1:first-child,
.ct-text .ct-h2:first-child,
.ct-text .ct-h3:first-child,
.ct-text h1:first-child,
.ct-text h2:first-child,
.ct-text h3:first-child {
    margin-top: 0;
}

/* adminListFilter reuses the compendium's .list-header-wrapper, .filter-input,
   .list-headers and .list-item__header--sortable styles (defined above). The
   only addition is letting per-list templates override the grid columns of
   .list-headers via inline style. */

/* ---------------------------------------------------------------------------
   TipTap editor — toolbar + content surface
   --------------------------------------------------------------------------- */

.tiptap-editor__toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--color-2-darkest);
    border-bottom: none;
    border-radius: 4px 4px 0 0;
    background: var(--color-2-darker);
}

.tiptap-editor__btn {
    min-width: 2rem;
    padding: 0.25rem 0.5rem;
    border: 1px solid transparent;
    border-radius: 3px;
    background: transparent;
    color: var(--color-4);
    font-size: 0.9rem;
    line-height: 1.2;
    cursor: pointer;
    transition: background-color 0.1s ease, border-color 0.1s ease;
}

.tiptap-editor__btn:hover {
    background: var(--color-2);
    border-color: var(--color-2-darkest);
}

.tiptap-editor__btn.is-active {
    background: var(--color-1);
    color: var(--color-2);
    border-color: var(--color-1);
}

.ct-context-menu {
    position: absolute;
    z-index: 1000;
    list-style: none;
    margin: 0;
    padding: 0.25rem 0;
    min-width: 11rem;
    background: var(--color-2);
    border: 1px solid var(--color-2-darkest);
    border-radius: 4px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
    font-size: 0.9rem;
}

.ct-context-menu__item {
    padding: 0.3rem 0.75rem;
    color: var(--color-4);
    cursor: pointer;
    user-select: none;
}

.ct-context-menu__item:hover {
    background: var(--color-1);
    color: var(--color-2);
}

.tiptap-editor__sep {
    width: 1px;
    height: 1.25rem;
    background: var(--color-2-darkest);
    margin: 0 0.25rem;
}

.tiptap-editor__surface {
    border: 1px solid var(--color-2-darkest);
    border-radius: 0 0 4px 4px;
    background: var(--color-2);
    min-height: 8rem;
    padding: 0.5rem 0.75rem;
}

.tiptap-editor__surface .ProseMirror {
    outline: none;
    min-height: 7rem;
}

.tiptap-editor__surface .ProseMirror:focus-visible {
    outline: none;
}

.ct-link {
    color: var(--color-1);
    font-weight: 600;
    text-decoration: underline;
    text-decoration-color: var(--color-31);
    text-underline-offset: 2px;
    cursor: pointer;
    transition: color 0.15s ease, text-decoration-color 0.15s ease;
}

.ct-link:hover {
    color: var(--color-1-lighter);
    text-decoration-color: var(--color-1-lighter);
}

.ct-link--unknown {
    color: var(--color-4-lighest);
    font-weight: 500;
    cursor: help;
    text-decoration: underline dashed var(--color-4-lighest);
    text-underline-offset: 2px;
}

.ct-link--unknown:hover {
    color: var(--color-4-lighter);
}

.ct-syntax-hint {
    margin-top: 0.5rem;
    padding: 0.5rem 0.75rem;
    background: rgba(186, 168, 152, 0.15);
    border-left: 3px solid var(--color-31);
    border-radius: 0 0.3rem 0.3rem 0;
    font-size: 0.85rem;
    color: var(--color-4-lighter);
}

.ct-syntax-hint > summary {
    cursor: pointer;
    font-weight: 600;
    color: var(--color-4);
    user-select: none;
}

.ct-syntax-hint__list {
    margin: 0.5rem 0 0 0;
    padding-left: 1rem;
    list-style: disc;
}

.ct-syntax-hint__list li {
    margin-bottom: 0.2rem;
    list-style: disc;
}

.ct-syntax-hint code {
    font-family: 'Courier New', Courier, monospace;
    background: rgba(78, 25, 25, 0.08);
    padding: 0.05rem 0.3rem;
    border-radius: 0.2rem;
    font-size: 0.85rem;
    color: var(--color-1);
}

/* --- Compendium [[ autocomplete popup -------------------------------- */

.ct-autocomplete {
    position: absolute;
    z-index: 10000;
    min-width: 220px;
    max-width: 360px;
    max-height: 240px;
    overflow-y: auto;
    background: var(--color-2);
    border: 1px solid var(--color-31);
    border-radius: 0.3rem;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
    padding: 0.25rem 0;
    margin: 0;
    list-style: none;
    font-size: 0.9rem;
}

.ct-autocomplete[hidden] {
    display: none;
}

.ct-autocomplete__item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.75rem;
    padding: 0.35rem 0.7rem;
    cursor: pointer;
    list-style: none;
    color: var(--color-4);
}

.ct-autocomplete__item[aria-selected="true"] {
    background: var(--color-1);
    color: var(--color-2);
}

.ct-autocomplete__primary {
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ct-autocomplete__secondary {
    color: var(--color-4-lighter);
    font-size: 0.8rem;
    text-transform: capitalize;
    flex-shrink: 0;
}

.ct-autocomplete__item[aria-selected="true"] .ct-autocomplete__secondary {
    color: var(--color-2-darkest);
}

.ct-autocomplete__empty {
    padding: 0.35rem 0.7rem;
    color: var(--color-4-lighter);
    font-style: italic;
    list-style: none;
}

.ct-caret-mirror {
    position: absolute;
    visibility: hidden;
    pointer-events: none;
    top: 0;
    left: -9999px;
    overflow: hidden;
    white-space: pre-wrap;
    word-wrap: break-word;
    overflow-wrap: break-word;
}

/* ====================================================================
   Combat Tracker  (.trk-*)
   ==================================================================== */

/* Small-button modifier (reused across tracker features) */
.btn--small {
    padding: 0.3rem 0.6rem;
    font-size: 0.8rem;
}

/* Two-step confirm state for destructive buttons. */
.btn--confirming {
    background: #b22222 !important;
    color: #fff !important;
    border-color: #8b1a1a !important;
}

/* List page (encounters + combatants) */
.trk-list-wrapper {
    width: 100%;
    max-width: 1100px;
    margin: 2rem auto;
    padding: 1.5rem 2rem;
    background: var(--color-2);
    border: 1px solid var(--color-31);
    border-radius: 0.4rem;
    max-height: calc(100vh - 14rem); /* viewport minus header/footer/margins */
    overflow-y: auto;
    align-self: flex-start;
}

.trk-list-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1rem;
}

.trk-list-header h1 {
    font-family: Georgia, 'Times New Roman', serif;
    color: var(--color-1);
    margin: 0;
}

.trk-list-header__actions {
    display: flex;
    gap: 0.5rem;
}

.trk-new-form {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    margin-bottom: 1.5rem;
    padding: 0.75rem;
    background: rgba(186, 168, 152, 0.15);
    border-radius: 0.3rem;
}

.trk-new-form label {
    font-weight: 600;
    color: var(--color-4);
}

.trk-new-form input[type="text"] {
    flex: 1;
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--color-31);
    border-radius: 0.3rem;
    background: var(--color-2-lighest);
    font-size: 0.95rem;
}

.trk-list-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9rem;
}

.trk-list-table thead th {
    text-align: left;
    padding: 0.5rem 0.7rem;
    background: var(--color-1);
    color: var(--color-2);
    font-weight: 600;
}

.trk-list-table tbody td {
    padding: 0.5rem 0.7rem;
    border-bottom: 1px solid var(--color-31);
}

.trk-list-table tbody tr:hover {
    background: rgba(186, 168, 152, 0.1);
}

.trk-list-table__actions {
    display: flex;
    gap: 0.4rem;
    flex-wrap: wrap;
}

.trk-empty {
    padding: 1rem;
    color: var(--color-4-lighter);
    font-style: italic;
    text-align: center;
}

/* Tracker page shell — fills available viewport so inner scroll has a bound */
.trk-page {
    width: 100%;
    max-width: 1200px;
    margin: 1.5rem auto;
    padding: 0 1rem;
    min-height: 0;
    display: flex;
    flex-direction: column;
    flex: 1;
}

.trk-page__nav {
    display: flex;
    gap: 1rem;
    margin-bottom: 0.75rem;
    flex-shrink: 0;
}

.trk-page__back {
    font-size: 0.9rem;
    color: var(--color-4);
}

.trk-page__back:hover {
    color: var(--color-1);
}

.trk-root {
    background: var(--color-2);
    border: 1px solid var(--color-31);
    border-radius: 0.4rem;
    padding: 1.25rem 1.5rem;
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* Body (flex column so the table scrolls within remaining space) */
.trk-body {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}

/* Header */
.trk-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 1rem;
    margin-bottom: 1rem;
    padding-bottom: 0.75rem;
    border-bottom: 2px solid var(--color-1);
    flex-shrink: 0;
}

.trk-header__title {
    margin: 0;
    font-family: Georgia, 'Times New Roman', serif;
    color: var(--color-1);
    font-size: 1.5rem;
    cursor: pointer;
}

.trk-header__title:hover {
    color: var(--color-1-lighter);
    text-decoration: underline;
    text-decoration-style: dotted;
}

.trk-header__meta {
    display: flex;
    gap: 0.5rem;
}

.trk-badge {
    display: inline-block;
    padding: 0.25rem 0.6rem;
    background: var(--color-4-lighter);
    color: var(--color-2);
    border-radius: 0.2rem;
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.02em;
}

.trk-badge--on {
    background: #27ae60;
}

.trk-badge--info {
    background: #e4c727;
}

.trk-badge--off {
    background: var(--color-31);
    color: var(--color-4);
}

/* Campaign attribution bar (TODO/32 #7) — a context strip above the encounter
   header; rolls made from this tracker attribute to the selected campaign. */
.trk-campaign-bar {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 1rem;
    padding-bottom: 0.75rem;
    border-bottom: 1px dashed var(--color-31);
    flex-shrink: 0;
}

.trk-campaign-bar__label {
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-4);
}

.trk-campaign-bar__select {
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--color-31);
    border-radius: 0.2rem;
    background: var(--color-2-lighest);
    font-size: 0.9rem;
    color: var(--color-4);
    font-family: inherit;
    min-width: 14rem;
    cursor: pointer;
}

.trk-campaign-bar__select:focus {
    outline: none;
    border-color: var(--color-1);
}

.trk-campaign-bar__hint {
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: #27ae60;
}

/* Endurance / fatigue */
.trk-endurance-cell {
    min-width: 7rem;
    vertical-align: top;
}

.trk-endurance-cell .trk-input--num {
    width: 4rem;
    margin-bottom: 0.25rem;
}

.trk-endurance {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    margin-top: 0.25rem;
    padding: 0.15rem 0.4rem;
    border-radius: 0.2rem;
    font-size: 0.75rem;
    font-weight: 600;
}

.trk-endurance--ok {
    background: var(--color-4-lighter);
    color: var(--color-2);
}

.trk-endurance--due {
    background: #e67e22;
    color: #fff;
    flex-wrap: wrap;
    padding: 0.25rem 0.4rem;
}

.trk-endurance--due .btn {
    padding: 0.1rem 0.35rem;
    font-size: 0.7rem;
}

.trk-fatigue {
    display: inline-block;
    margin-top: 0.25rem;
    padding: 0.2rem 0.5rem;
    border: 0;
    border-radius: 0.2rem;
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    cursor: pointer;
    user-select: none;
}

.trk-fatigue:hover { filter: brightness(1.1); }
.trk-fatigue:active { filter: brightness(0.9); }

.trk-fatigue--fresh { background: var(--color-4-lighter); color: var(--color-2); }
.trk-fatigue--mild { background: #e4c727; color: #222; }
.trk-fatigue--severe { background: #d35400; color: #fff; }
.trk-fatigue--critical { background: #8e0000; color: #fff; }

.trk-fatigue--check {
    animation: trk-fatigue-pulse 1.1s ease-in-out infinite;
    box-shadow: 0 0 0 0 rgba(230, 126, 34, 0.6);
}

@keyframes trk-fatigue-pulse {
    0%   { box-shadow: 0 0 0 0   rgba(230, 126, 34, 0.7); }
    50%  { box-shadow: 0 0 0 6px rgba(230, 126, 34, 0);   }
    100% { box-shadow: 0 0 0 0   rgba(230, 126, 34, 0);   }
}

/* Controls */
.trk-controls {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-bottom: 1rem;
    flex-shrink: 0;
}

/* Scroll container for the participants table — fills remaining height */
.trk-table-scroll {
    flex: 1;
    min-height: 0;
    overflow: auto;
    border: 1px solid var(--color-31);
    border-radius: 0.3rem;
    background: var(--color-2-lighest);
}

/* Participants table */
.trk-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9rem;
}

.trk-table thead th {
    padding: 0.5rem 0.5rem;
    text-align: left;
    background: var(--color-1);
    color: var(--color-2);
    font-weight: 600;
    white-space: nowrap;
    position: sticky;
    top: 0;
    z-index: 2;
}

.trk-table tbody td {
    padding: 0.4rem 0.5rem;
    border-bottom: 1px solid var(--color-31);
    vertical-align: middle;
}

.trk-row--active > td {
    background: rgba(255, 127, 17, 0.12);
}

.trk-row--active > td:first-child {
    box-shadow: inset 3px 0 0 var(--color-3);
}

.trk-row--inactive {
    opacity: 0.55;
}

.trk-row--defeated {
    opacity: 0.35;
    pointer-events: none;
}

/* Keep Revive, Remove, and the notes toggle actionable so the GM can
   recover, clean up, or jot last-words on a corpse. */
.trk-row--defeated [data-action="toggle-defeated"],
.trk-row--defeated [data-action="remove-participant"],
.trk-row--defeated [data-action="toggle-notes"] {
    pointer-events: auto;
}

.trk-row--defeated .trk-input[data-field="name"] {
    text-decoration: line-through;
}

.trk-input {
    width: 100%;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--color-31);
    border-radius: 0.2rem;
    background: var(--color-2-lighest);
    font-size: 0.9rem;
    color: var(--color-4);
    font-family: inherit;
}

.trk-input--num {
    width: 4.5rem;
    text-align: center;
}

.trk-input--init {
    width: 5rem;
    text-align: center;
    font-family: 'Courier New', Courier, monospace;
}

.trk-input--notes {
    min-width: 12rem;
    resize: vertical;
}

.trk-notes-toggle-cell {
    text-align: center;
    width: 3rem;
}

.trk-notes-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.15rem;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 0.35rem;
    padding: 0.2rem 0.4rem;
    cursor: pointer;
    opacity: 0.45;
    font-size: 0.95rem;
    line-height: 1;
    transition: opacity 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}

.trk-notes-toggle:hover {
    background: var(--color-2-darker);
    opacity: 1;
}

.trk-notes-toggle--filled {
    opacity: 1;
    border-color: var(--color-1-lighter);
    color: var(--color-1);
}

.trk-notes-toggle__chev {
    font-size: 0.7rem;
}

.trk-notes-row > td {
    background: var(--color-2-lighter);
    padding: 0.5rem 1rem 0.75rem 1rem;
    border-top: none;
}

.trk-notes-row textarea {
    width: 100%;
    min-height: 4rem;
    resize: vertical;
}

.trk-notes-row[hidden] {
    display: none;
}

.trk-init-cell {
    white-space: nowrap;
}

.trk-init-rolled {
    margin-left: 0.3rem;
    color: var(--color-1);
    font-weight: 600;
    font-size: 0.85rem;
}

/* Dots + stepper */
.trk-dots-cell {
    white-space: nowrap;
}

.trk-dots-inline {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
}

.trk-step-btn {
    width: 1.6rem;
    height: 1.6rem;
    border-radius: 50%;
    border: 1px solid var(--color-31);
    background: var(--color-2-lighest);
    color: var(--color-4);
    font-size: 0.9rem;
    line-height: 1;
    cursor: pointer;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.trk-step-btn:hover {
    background: var(--color-1);
    color: var(--color-2);
    border-color: var(--color-1);
}

.trk-dots {
    display: inline-flex;
    gap: 2px;
    padding: 0 0.2rem;
}

.trk-dot {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    display: inline-block;
    cursor: pointer;
    border: 1px solid rgba(0, 0, 0, 0.2);
    opacity: 0.35;
    transition: opacity 0.1s ease, transform 0.1s ease;
}

.trk-dot:hover {
    transform: scale(1.18);
}

.trk-dot--ap {
    background: #2ecc71;
}

.trk-dot--dap {
    background: #3498db;
}

.trk-dot--magic_points {
    background: #9b59b6;
}

.trk-dot--luck_points {
    background: #f1c40f;
}

.trk-dot--filled {
    opacity: 1;
    box-shadow: 0 0 3px rgba(0, 0, 0, 0.25);
}

/* Compact "current/max" pill used in place of the dots wall when max ≥ 10.
   Shares the click semantics of the dots wrapper (left-click −1, right-click
   +1); colored left border keys the pill to its resource type. */
.trk-dot-text {
    display: inline-block;
    padding: 0.2rem 0.55rem;
    border-radius: 999px;
    border: 1px solid var(--color-31);
    /*background: var(--color-3, var(--color-2));*/
    color: var(--color-1);
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
    min-width: 3.5rem;
    text-align: center;
    font-family: inherit;
    line-height: 1.2;
}

.trk-dot-text:hover {
    border-color: var(--color-4);
}

.trk-dot-text--ap            { border-left: 5px solid #2ecc71; }
.trk-dot-text--dap           { border-left: 5px solid #3498db; }
.trk-dot-text--magic_points  { border-left: 5px solid #9b59b6; }
.trk-dot-text--luck_points   { border-left: 5px solid #f1c40f; }

/* Copy dialog */
.trk-dialog {
    border: 1px solid var(--color-31);
    border-radius: 0.4rem;
    padding: 0;
    background: var(--color-2);
    color: var(--color-4);
    max-width: 400px;
}

.trk-dialog::backdrop {
    background: rgba(0, 0, 0, 0.35);
}

.trk-dialog__form {
    padding: 1.25rem 1.5rem;
}

.trk-dialog__form h3 {
    margin: 0 0 0.5rem 0;
    color: var(--color-1);
    font-family: Georgia, 'Times New Roman', serif;
}

.trk-dialog__form p {
    margin: 0 0 1rem 0;
}

.trk-dialog__buttons {
    display: flex;
    gap: 0.5rem;
    padding: 0;
    margin: 0;
    justify-content: flex-end;
    flex-wrap: wrap;
}

/* Picker modal */
.trk-picker {
    padding: 0.5rem 0.25rem;
}

.trk-picker__title {
    margin: 0 0 0.25rem 0;
    font-family: Georgia, 'Times New Roman', serif;
    color: var(--color-1);
}

.trk-picker__hint {
    margin: 0 0 0.75rem 0;
    font-size: 0.85rem;
    color: var(--color-4-lighter);
}

.trk-picker__search {
    width: 100%;
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--color-31);
    border-radius: 0.3rem;
    background: var(--color-2-lighest);
    margin-bottom: 0.75rem;
    font-size: 0.95rem;
}

.trk-picker__group {
    margin-bottom: 0.75rem;
}

.trk-picker__group-title {
    margin: 0 0 0.35rem 0;
    padding-bottom: 0.2rem;
    font-size: 0.95rem;
    color: var(--color-1);
    border-bottom: 1px solid var(--color-31);
}

.trk-picker__list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.trk-picker__item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0.25rem;
    border-bottom: 1px dotted var(--color-31);
}

.trk-picker__item:last-child {
    border-bottom: none;
}

.trk-picker__item-main {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    min-width: 0;
}

.trk-picker__item-name {
    font-weight: 600;
    color: var(--color-4);
}

.trk-picker__item-meta {
    font-size: 0.8rem;
    color: var(--color-4-lighter);
}

.trk-hit-locations {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
}

.trk-hit-loc {
    font-size: 0.7rem;
    padding: 0.15rem 0.3rem;
    border: 1px solid var(--color-4-lighter);
    border-radius: 3px;
    background: var(--color-bg);
    cursor: pointer;
    transition: background 0.15s;
}

.trk-hit-loc:hover {
    border-color: var(--color-4);
    box-shadow: 0 0 0 1px var(--color-4) inset;
}

.trk-hit-loc--healthy {
    background: #d4edda;
    border-color: #3a7d3a;
    color: #155724;
}

.trk-hit-loc--minor {
    background: #fff3cd;
    border-color: #b58a00;
    color: #856404;
}

.trk-hit-loc--serious {
    background: #f8d7da;
    border-color: #c0392b;
    color: #721c24;
}

.trk-hit-loc--major {
    background: #3d0000;
    border-color: #6b0000;
    color: #ffd9d9;
    font-style: italic;
}

.trk-hitloc-cell {
    min-width: 120px;
}

.trk-hit-loc {
    position: relative;
}

.trk-hit-loc__badge {
    position: absolute;
    top: -0.5rem;
    right: -0.5rem;
    min-width: 1.4rem;
    padding: 0 0.3rem;
    height: 1.4rem;
    line-height: 1.4rem;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: bold;
    text-align: center;
    color: #fff;
    pointer-events: none;
    box-shadow: 0 1px 3px rgba(0,0,0,0.3);
    z-index: 2;
}

.trk-hit-loc__badge--damage {
    background: #c0392b;
}

.trk-hit-loc__badge--heal {
    background: #2e8b57;
}

.trk-hitloc-popover {
    position: absolute;
    z-index: 1000;
    background: var(--color-2);
    border: 1px solid #dadada;
    border-radius: 0.5rem;
    box-shadow: 0 4px 12px rgba(0,0,0,0.2);
    padding: 0.6rem;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    min-width: 180px;
}

.trk-hitloc-popover__input {
    width: 100%;
    padding: 0.4rem 0.6rem;
    font-size: 1rem;
    border: 1.5px solid var(--color-2-darkest);
    border-radius: 0.3rem;
}

.trk-hitloc-popover__input:focus {
    outline: none;
    border-color: var(--color-1);
    box-shadow: 0 0 0 3px rgba(78, 25, 25, 0.12);
}

.trk-hitloc-popover__actions {
    display: flex;
    gap: 0.4rem;
    flex-wrap: wrap;
}

.trk-hitloc-popover__actions .btn {
    flex: 1;
    min-width: 0;
}

/* ====================================================================
   Tracker layout — two-pane (cards rail + detail column)
   ==================================================================== */

.trk-page--wide {
    max-width: 1600px;
}

.trk-main {
    display: flex;
    flex: 1;
    min-height: 0;
    gap: 1rem;
    margin-top: 0.75rem;
}

.trk-cards {
    flex: 0 0 280px;
    min-width: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding-right: 0.25rem;
}

.trk-card {
    background: var(--color-2);
    border: 1px solid var(--color-31);
    border-left: 4px solid transparent;
    border-radius: 0.3rem;
    padding: 0.6rem 0.75rem;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    transition: background 0.1s ease, border-color 0.1s ease;
}

.trk-card:hover {
    background: var(--color-2-light, var(--color-2));
    border-color: var(--color-4);
}

.trk-card--active {
    border-left-color: var(--color-orange, #d35400);
    background: var(--color-2-light, var(--color-2));
}

.trk-card--selected {
    outline: 2px solid var(--color-4);
    outline-offset: -1px;
}

.trk-card--inactive {
    opacity: 0.7;
}

.trk-card--defeated {
    opacity: 0.5;
    text-decoration: line-through;
}

.trk-card__head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.5rem;
}

.trk-card__name {
    font-weight: 600;
    color: var(--color-1);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1;
    min-width: 0;
}

.trk-card__init {
    font-size: 0.85rem;
    color: var(--color-4);
    flex-shrink: 0;
}

.trk-card__init-val {
    font-weight: 600;
    color: var(--color-1);
}

.trk-card__init-val--formula {
    font-weight: 400;
    color: var(--color-4);
    font-style: italic;
}

.trk-card__init-empty {
    font-style: italic;
    opacity: 0.6;
}

.trk-card__stats {
    display: flex;
    gap: 0.4rem;
}

.trk-card__stat {
    flex: 1;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.3rem;
    background: var(--color-3, var(--color-2));
    border: 1px solid var(--color-31);
    border-radius: 0.25rem;
    padding: 0.2rem 0.5rem;
    font-size: 0.8rem;
    color: var(--color-1);
    cursor: pointer;
    font-family: inherit;
}

.trk-card__stat:hover {
    border-color: var(--color-4);
    background: var(--color-2-light, var(--color-2));
}

.trk-card__stat-label {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-4);
}

.trk-card__stat-val {
    font-weight: 600;
}

.trk-card__hp {
    display: flex;
    gap: 0.4rem;
    align-items: baseline;
    font-size: 0.85rem;
    color: var(--color-1);
}

.trk-card__hp-label {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-4);
}

.trk-card__hp-val {
    font-family: var(--font-mono, ui-monospace, monospace);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

.trk-card__badges {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}

.trk-card__badge {
    font-size: 0.7rem;
    padding: 0.1rem 0.4rem;
    border-radius: 0.2rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    background: var(--color-31);
    color: var(--color-1);
}

.trk-card__badge--defeated {
    background: #8e0000;
    color: #fff;
}

.trk-card__badge--mild {
    background: #e4c727;
    color: #222;
}

.trk-card__badge--severe {
    background: #d35400;
    color: #fff;
}

.trk-card__badge--critical {
    background: #8e0000;
    color: #fff;
}

.trk-right {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 1rem;
    overflow: hidden;
}

.trk-detail-top {
    flex: 0 0 auto;
    background: var(--color-2);
    border: 1px solid var(--color-31);
    border-radius: 0.3rem;
    padding: 1rem;
    overflow-y: auto;
    max-height: 45%;
}

.trk-detail-empty {
    color: var(--color-4);
    text-align: center;
    padding: 2rem 1rem;
    font-style: italic;
}

.trk-detail:not([hidden]) {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.trk-detail--active {
    /* active gets a subtle ribbon — actual active visual still lives on cards */
}

.trk-detail--defeated {
    opacity: 0.7;
}

.trk-detail__head {
    display: flex;
    gap: 0.75rem;
    align-items: center;
    padding-bottom: 0.6rem;
    border-bottom: 1px solid var(--color-31);
}

.trk-detail__name {
    flex: 1;
    font-size: 1.15rem;
    font-weight: 600;
    background: transparent;
    border: 1px solid transparent;
    padding: 0.25rem 0.4rem;
}

.trk-detail__name:hover,
.trk-detail__name:focus {
    background: var(--color-3, var(--color-2));
    border-color: var(--color-31);
}

.trk-detail__head-actions {
    display: flex;
    gap: 0.5rem;
    flex-shrink: 0;
}

.trk-detail__section {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.trk-detail__heading {
    margin: 0;
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--color-4);
    border-bottom: 1px solid var(--color-31);
    padding-bottom: 0.2rem;
}

.trk-detail__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 0.75rem 1.25rem;
    align-items: start;
}

.trk-detail__field {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.3rem;
    min-width: 0;
}

.trk-detail__label {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-4);
}

.trk-statblock {
    flex: 1;
    min-height: 0;
    background: var(--color-2);
    border: 1px solid var(--color-31);
    border-radius: 0.3rem;
    padding: 1rem;
    overflow-y: auto;
}

.trk-statblock__empty {
    color: var(--color-4);
    text-align: center;
    font-style: italic;
    padding: 2rem 1rem;
}

.trk-statblock__head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 1rem;
    padding-bottom: 0.6rem;
    margin-bottom: 0.75rem;
    border-bottom: 1px solid var(--color-31);
}

.trk-statblock__title {
    margin: 0;
    font-size: 1.25rem;
    color: var(--color-1);
}

.trk-statblock__subtitle {
    font-size: 0.85rem;
    color: var(--color-4);
    margin-top: 0.15rem;
}

.trk-statblock__body {
    /* Inherit existing admin table styling — no overrides needed */
}

/* Two-column grid for the top of the statblock body:
   Characteristics & Attributes on the left, Hit Locations on the right.
   Collapses to single column on narrow viewports. */
.statblock-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem 1.5rem;
    align-items: start;
}

.statblock-grid__col {
    min-width: 0;
}

/* Characteristics + Attributes tables side-by-side inside the left column. */
.statblock-subgrid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.75rem;
    align-items: start;
}

.statblock-subgrid > table {
    min-width: 0;
}

@media (max-width: 60rem) {
    .statblock-grid {
        grid-template-columns: 1fr;
    }
    .statblock-subgrid {
        grid-template-columns: 1fr;
    }
}

/* ====================================================================
   Mobile responsiveness
   ==================================================================== */

/* Tablet and below */
@media (max-width: 60rem) {
    .container {
        max-width: 100%;
        width: 100%;
        min-width: 0;
        margin-top: 1rem;
        margin-bottom: 1rem;
        padding: 1rem;
    }

    .header { height: auto; }

    .navbar {
        position: relative;
        display: grid;
        grid-template-columns: 44px 1fr;
        align-items: center;
        gap: 0;
        padding: 0 0.75rem;
        height: 3.25rem;
        overflow: visible;
    }

    .navbar__menu-toggle {
        display: inline-flex;
        grid-column: 1;
    }
    .navbar__brand {
        display: inline-block;
        grid-column: 2;
    }

    /* Drawer overlays the page rather than pushing content down. */
    .navbar__drawer {
        display: none;
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        flex-direction: column;
        background: var(--color-1);
        border-top: 1px solid rgba(0,0,0,0.2);
        box-shadow: 0 6px 12px rgba(0,0,0,0.18);
        z-index: 30;
        max-height: calc(100vh - 3.25rem);
        overflow-y: auto;
    }
    .navbar--open .navbar__drawer { display: flex; }

    .navbar__center,
    .navbar__right {
        display: flex;
        flex-direction: column;
        height: auto;
        justify-content: flex-start;
        grid-column: auto;
    }
    .navbar__right {
        border-top: 1px solid rgba(0,0,0,0.35);
    }

    .navbar__center a {
        background: var(--color-1-darker);
        flex: none;
    }
    .navbar__btn {
        padding: 0.85rem 1rem;
        min-height: 44px;
        justify-content: flex-start;
        width: 100%;
    }
    .navbar__right .navbar__btn { background: var(--color-1-darker); }

    .notepad {
        position: static;
        width: 100%;
        padding: 1rem;
        top: auto;
        right: auto;
        border-width: 0.25rem;
        margin: 1rem 0;
    }

    .characters-display {
        width: 100%;
        gap: 1rem;
    }

    .character-card {
        max-width: 100%;
    }

    /* Tables: allow horizontal scroll rather than blowing out the layout */
    .trk-list-wrapper,
    .trk-list-table,
    .trk-table {
        max-width: 100%;
    }

    .trk-list-table,
    .trk-table {
        display: block;
        overflow-x: auto;
        white-space: nowrap;
    }

    .errorImage {
        width: 100%;
        max-width: 512px;
        height: auto;
        aspect-ratio: 1 / 1;
        background-size: contain;
        background-repeat: no-repeat;
    }
}

/* Phone */
@media (max-width: 40rem) {
    .container {
        padding: 0.75rem;
        border-width: 1px;
    }

    .errorHeader {
        font-size: 1.75rem;
    }

    .errorBody {
        font-size: 1.1rem;
    }
}

/* Touch devices: bigger tap targets, no hover-dependent affordances */
@media (hover: none) {
    .btn--small {
        padding: 0.75rem 1rem;
        font-size: 0.9rem;
    }

    .navbar__btn {
        min-height: 44px;
    }

    .character-card__bottom > * {
        min-height: 44px;
        min-width: 44px;
    }

    /* Replace hover affordances with active feedback so touch users get cues */
    .navbar__center a:active {
        background-color: var(--color-1-lightest);
    }

    .character-card__bottom > *:active {
        background-color: var(--color-2-darker);
    }

    .trk-list-table tbody tr:active {
        background: rgba(186, 168, 152, 0.2);
    }

    .trk-hit-loc:active {
        background: var(--color-2-light);
    }
}
/* ---------------------------------------------------------------------------
   Notes FAB — floating action button rendered from layouts/main.ejs
   Visible to user.type >= 2 (Assistant GM, GM, Admin).
--------------------------------------------------------------------------- */
/* Global 3D dice tray overlay: full-viewport, transparent (the engine clears
   to alpha 0), and click-through so it never blocks the UI underneath. */
.dice-tray {
    position: fixed;
    inset: 0;
    /* Sits above EVERYTHING — including the sideModal (9999) and the compendium
       autocomplete (10000) — so dice always tumble in view, whatever surface
       triggered the roll. Safe because the overlay is pointer-events:none, so it
       never blocks interaction with what's beneath it. */
    z-index: 100000;
    pointer-events: none;
}
.dice-tray canvas {
    pointer-events: none;
}

/* Dice-look preview sandbox (TODO/0 #30). The preview tray is a contained box,
   unlike the global full-viewport overlay. */
.dice-preview { padding: 1rem 1.25rem; }
.dice-preview__controls {
    display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.75rem;
    margin: 0.75rem 0 1rem;
}
.dice-preview__field { display: flex; flex-direction: column; gap: 0.25rem; }
.dice-preview__label {
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; color: #6a5d44;
}
.dice-preview__tray {
    position: relative;
    width: 100%;
    height: 60vh;
    min-height: 360px;
    border: 1px solid #d8cfba;
    border-radius: 0.6rem;
    overflow: hidden;
    background: #1f2a1f;
}
.dice-preview__tray canvas { display: block; }

/* Dice-roll node editor popover (TipTap 🎲 button). */
.dice-editor {
    position: absolute;
    z-index: 2000;
    width: 18rem;
    max-width: calc(100vw - 1rem);
    background: #fffcfc;
    border: 1px solid #6a5d44;
    border-radius: 8px;
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.25);
    padding: 0.6rem;
    font-size: 0.85rem;
}
.dice-editor__types {
    display: flex;
    gap: 0.25rem;
    margin-bottom: 0.5rem;
}
.dice-editor__type-btn {
    flex: 1;
    padding: 0.3rem 0.4rem;
    border: 1px solid #6a5d44;
    background: #f0e9dd;
    color: #4E1919;
    border-radius: 5px;
    cursor: pointer;
}
.dice-editor__type-btn.is-active {
    background: #4E1919;
    color: #e4ddd0;
}
.dice-editor__row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.4rem;
}
.dice-editor__label {
    width: 5rem;
    flex: none;
    opacity: 0.85;
}
.dice-editor__input {
    flex: 1;
    min-width: 0;
    padding: 0.25rem 0.4rem;
    border: 1px solid #6a5d44;
    border-radius: 4px;
    background: #fff;
}
.dice-editor__hidden {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0.3rem 0;
}
.dice-editor__error {
    color: #4E1919;
    font-weight: 600;
    margin: 0.2rem 0;
}
.dice-editor__actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.4rem;
    margin-top: 0.5rem;
}

/* Roll-result feedback toasts (lower-right). */
.dice-toast-stack {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 100;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    align-items: flex-end;
    pointer-events: none;
}
.dice-toast {
    pointer-events: auto;
    cursor: pointer;
    width: 150px;
    box-sizing: border-box;
    background: #2a2320;
    color: #e4ddd0;
    border-radius: 8px;
    padding: 0.5rem 0.6rem;
    border-left: 4px solid #6a5d44;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
    opacity: 0;
    transform: translateX(20px);
    transition: opacity 0.2s ease, transform 0.2s ease;
}
.dice-toast--in { opacity: 1; transform: translateX(0); }
.dice-toast--leaving { opacity: 0; transform: translateX(20px); }
.dice-toast__title {
    font-size: 0.7rem;
    opacity: 0.85;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.dice-toast__big { font-size: 1.3rem; font-weight: 800; line-height: 1.15; margin: 0.1rem 0; }
.dice-toast__sub { font-size: 0.68rem; opacity: 0.72; }
.dice-toast--critical { border-left-color: #1f9d3a; }
.dice-toast--critical .dice-toast__big { color: #67d27f; }
.dice-toast--success  { border-left-color: #2e8b3d; }
.dice-toast--success .dice-toast__big  { color: #84d28e; }
.dice-toast--failure  { border-left-color: #c0612f; }
.dice-toast--failure .dice-toast__big  { color: #ec9466; }
.dice-toast--fumble   { border-left-color: #7d1414; }
.dice-toast--fumble .dice-toast__big   { color: #e35a5a; }
.dice-toast--damage   { border-left-color: #4E1919; }
.dice-toast--hidden .dice-toast__title { font-style: italic; }

/* Roll buttons on a creature statblock in the Combat Tracker. */
.statblock-rolls {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
}
.stat-roll {
    cursor: pointer;
    font: inherit;
    color: inherit;
    border: 1px solid rgba(78, 25, 25, 0.35);
    border-radius: 5px;
    padding: 0.05rem 0.4rem;
    background: rgba(78, 25, 25, 0.06);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
    transition: background 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease, transform 0.06s ease;
}
.stat-roll:hover,
.stat-roll:focus-visible {
    background: rgba(78, 25, 25, 0.14);
    border-color: rgba(78, 25, 25, 0.6);
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.18);
    outline: none;
}
.stat-roll:active {
    transform: translateY(1px);
    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
/* Shifted target value (grade / fatigue) — tinted + italic like the sheet. */
.stat-roll--harder .stat-roll__value { color: #c0392b; font-style: italic; }
.stat-roll--easier .stat-roll__value { color: #2e8b3d; font-style: italic; }

/* Manual difficulty selector above a Combat Tracker statblock. */
.trk-difficulty {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    margin-bottom: 0.6rem;
}
.trk-diff-btn {
    font: inherit;
    font-size: 0.78rem;
    cursor: pointer;
    padding: 0.15rem 0.5rem;
    border: 1px solid #6a5d44;
    border-radius: 5px;
    background: #f0e9dd;
    color: #4E1919;
    transition: background 0.12s ease, color 0.12s ease;
}
.trk-diff-btn:hover { background: #e6dcca; }
.trk-diff-btn--active {
    background: #4E1919;
    color: #e4ddd0;
    border-color: #4E1919;
}

/* Difficulty menu for skill chips (right-click / long-press). */
.dice-diff-menu {
    position: absolute;
    z-index: 2100;
    min-width: 9rem;
    background: #fffcfc;
    border: 1px solid #6a5d44;
    border-radius: 7px;
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.25);
    padding: 0.25rem;
    font-size: 0.85rem;
}
.dice-diff-menu__header {
    padding: 0.2rem 0.5rem 0.35rem;
    font-weight: 700;
    color: #4E1919;
    border-bottom: 1px solid #e4ddd0;
    margin-bottom: 0.2rem;
}
.dice-diff-menu__btn {
    display: block;
    width: 100%;
    text-align: left;
    padding: 0.3rem 0.5rem;
    border: 0;
    background: none;
    border-radius: 4px;
    cursor: pointer;
    color: #2a2320;
}
.dice-diff-menu__btn:hover,
.dice-diff-menu__btn:focus-visible {
    background: rgba(78, 25, 25, 0.12);
    outline: none;
}

/* Inline dice-roll chip (TipTap diceRoll node) — clickable in read-only views. */
.dice-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.2em;
    padding: 0.02em 0.45em;
    margin: 0 0.08em;
    background: #4E1919;
    color: #e4ddd0;
    border-radius: 0.4em;
    font-size: 0.92em;
    line-height: 1.4;
    white-space: nowrap;
    cursor: pointer;
    user-select: none;
}
.dice-chip:hover,
.dice-chip:focus-visible {
    background: #6a2222;
    outline: none;
}
.dice-chip--hidden {
    font-style: italic;
    opacity: 0.9;
}

/* Bottom-left dice-roller FAB: a toggle with a dice menu that opens upward. */
.dice-roller {
    position: fixed;
    left: 1rem;
    bottom: 1rem;
    z-index: 95; /* above the dice-tray overlay so controls stay clickable */
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
}
.dice-roller__menu {
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
}
/* The explicit display above would otherwise defeat the [hidden] attribute. */
.dice-roller__menu[hidden] {
    display: none;
}
.dice-roller__die,
.dice-roller__toggle {
    width: 3rem;
    height: 3rem;
    padding: 0;
    border: 0;
    background: none;
    border-radius: 50%;
    cursor: pointer;
    position: relative;
    filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.5));
    transition: transform 0.12s ease;
}
.dice-roller__toggle {
    width: 3.5rem;
    height: 3.5rem;
}
.dice-roller__die:hover,
.dice-roller__die:focus-visible,
.dice-roller__toggle:hover,
.dice-roller__toggle:focus-visible {
    transform: scale(1.12);
    outline: none;
}
.dice-roller__die:active {
    transform: scale(0.95);
}
.dice-roller__icon {
    width: 100%;
    height: 100%;
    display: block;
    pointer-events: none;
}

/* Roll privacy selector — a small pill at the bottom of the dice menu whose
   options fly out laterally (to the right, since the FAB hugs the left edge). */
.dice-roller__privacy {
    position: relative;
    margin-top: 0.25rem;
}
.dice-roller__privacy-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.25rem;
    width: 3rem;
    height: 1.6rem;
    padding: 0 0.35rem;
    border: 0;
    border-radius: 0.8rem;
    background: rgba(30, 30, 35, 0.92);
    color: #fff;
    font-size: 0.6rem;
    cursor: pointer;
    filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.5));
}
.dice-roller__privacy-current { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dice-roller__privacy-caret { opacity: 0.7; }
.dice-roller__privacy-menu {
    position: absolute;
    left: 100%;
    bottom: 0;
    margin-left: 0.5rem;
    min-width: 13rem;
    background: #fff;
    border: 1px solid #aaa;
    border-radius: 0.4rem;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
    z-index: 1000;
    overflow: hidden;
}
.dice-roller__privacy-menu[hidden] { display: none; }
.dice-roller__privacy-option {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.05rem;
    width: 100%;
    padding: 0.4rem 0.6rem;
    border: 0;
    background: none;
    text-align: left;
    cursor: pointer;
}
.dice-roller__privacy-option:hover { background: #f0f0f0; }
.dice-roller__privacy-option.is-active { background: #6a5d44; }
.dice-roller__privacy-option.is-active .dice-roller__privacy-label,
.dice-roller__privacy-option.is-active .dice-roller__privacy-desc { color: #fff; }
.dice-roller__privacy-label { font-weight: 600; font-size: 0.85rem; }
.dice-roller__privacy-desc { font-size: 0.72rem; color: #777; }
/* Pop the dice in when the menu opens. */
.dice-roller__menu:not([hidden]) .dice-roller__die {
    animation: dice-roller-pop 0.16s ease both;
}
@keyframes dice-roller-pop {
    from { opacity: 0; transform: translateY(0.5rem) scale(0.85); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}
.dice-roller__count {
    position: absolute;
    top: -2px;
    right: -2px;
    min-width: 1.15rem;
    height: 1.15rem;
    padding: 0 0.2rem;
    box-sizing: border-box;
    background: #e4ddd0;
    color: #4E1919;
    border: 1px solid #4E1919;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 700;
    line-height: 1.05rem;
    text-align: center;
}

@media (max-width: 60rem) {
    /* Bottom-left dice-roller FAB: a toggle with a dice menu that opens upward. */
    .dice-roller {
        bottom: 3rem;        
    }
}

.notes-fab {
    position: fixed;
    left: 0;
    top: 10rem;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.5rem;
    z-index: 80;
    /* Park the FAB mostly off-screen on the left; ~2.75rem peeks out as a
       hover target.  Sliding back to 0 on hover/focus reveals the full
       button. */
    transform: translateX(calc(-100% + 2.75rem));
    transition: transform 0.25s ease;
}

/* Reveal on hover, on keyboard focus inside the FAB, and while the popover
   is open (popover open => toggle button has aria-expanded="true"). */
.notes-fab:hover,
.notes-fab:focus-within,
.notes-fab:has([data-quicknote-toggle][aria-expanded="true"]) {
    transform: translateX(0);
}

.notes-fab__btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.6rem 1rem;
    border-radius: 0 999px 999px 0;
    border: 1.5px solid transparent;
    font-family: inherit;
    font-weight: bold;
    font-size: 0.9rem;
    text-decoration: none;
    cursor: pointer;
    box-shadow: 4px 4px 12px rgba(0, 0, 0, 0.18);
    transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}

.notes-fab__btn:active {
    transform: scale(1.08);
}

.notes-fab__btn--primary {
    background: var(--color-1);
    color: #fafafa;
    border-color: var(--color-1-darkest);
}

.notes-fab__btn--primary:hover {
    background: var(--color-1-darkest);
}

.notes-fab__btn--secondary {
    background: #fffcfc;
    color: var(--color-1);
    border-color: var(--color-1-lighter);
}

.notes-fab__btn--secondary:hover {
    background: rgba(78, 25, 25, 0.06);
    border-color: var(--color-1);
}

.notes-fab__icon {
    font-size: 1.1rem;
    line-height: 1;
}

.notes-fab__label {
    line-height: 1;
}

/* Audit-log FAB. Mirror of the notes-fab but parked on the right side so
   the sideModal it opens slides in from the same edge. */
.audit-fab {
    position: fixed;
    right: 0;
    top: 10rem;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.5rem;
    z-index: 80;
    transform: translateX(calc(100% - 2.75rem));
    transition: transform 0.25s ease;
}

.audit-fab:hover,
.audit-fab:focus-within {
    transform: translateX(0);
}

.audit-fab__btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.6rem 1rem;
    border-radius: 999px 0 0 999px;
    border: 1.5px solid var(--color-1-darkest);
    background: var(--color-1);
    color: #fafafa;
    font-family: inherit;
    font-weight: bold;
    font-size: 0.9rem;
    cursor: pointer;
    box-shadow: -4px 4px 12px rgba(0, 0, 0, 0.18);
    transition: background-color 0.15s ease, transform 0.1s ease;
}

.audit-fab__btn:hover {
    background: var(--color-1-darkest);
}

.audit-fab__btn:active {
    transform: scale(1.08);
}

.audit-fab__icon {
    font-size: 1.1rem;
    line-height: 1;
}

.audit-fab__label {
    line-height: 1;
}

/* ===================================================================
   Audit log viewer
   =================================================================== */

.audit-log__filters {
    background: var(--color-2);
    border: 1px solid #dadada;
    border-radius: 0.6rem;
    padding: 1rem 1.1rem 0.85rem;
    margin-bottom: 1rem;
    box-shadow: 0 1px 3px rgba(0,0,0,0.06);
}

.audit-log__filter-disclosure > summary {
    cursor: pointer;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: #6a5d44;
    padding: 0.2rem 0;
    margin-bottom: 0.5rem;
    border-bottom: 1px solid #e4ddd0;
    user-select: none;
}

.audit-log__filter-disclosure[open] > summary {
    margin-bottom: 0.75rem;
}

.audit-log__meta {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    font-size: 0.8rem;
    color: var(--color-4-lighter);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 0 0.25rem 0.5rem;
    border-bottom: 1px solid #e4ddd0;
    margin-bottom: 0.75rem;
}

.audit-log__list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.audit-log__card {
    display: flex;
    align-items: flex-start;
    gap: 0.85rem;
    background: var(--color-2);
    border: 1px solid #dadada;
    border-radius: 0.6rem;
    padding: 0.75rem 0.9rem;
    box-shadow: 0 1px 3px rgba(0,0,0,0.06);
    transition: box-shadow 0.15s ease, border-color 0.15s ease;
}

.audit-log__card:hover {
    box-shadow: 0 3px 8px rgba(0,0,0,0.12);
    border-color: var(--color-2-darkest, #c8c8c8);
}

/* Roll-visibility border (TODO/0 #roll-privacy). A dice roll's audience is
   readable at a glance via a thicker 3px border tinted by privacy. Public keeps
   the neutral colour; private goes blue (with a blue-tinted background); secret
   and hidden go near-black. :hover variants stop the generic card hover from
   washing the tint out. */
.audit-log__card--public,
.audit-log__card--private,
.audit-log__card--secret,
.audit-log__card--hidden {
    border-width: 3px;
}
.audit-log__card--private {
    border-color: #3f6fd1;
    background: #eaf1fb;
}
.audit-log__card--private:hover { border-color: #2f5cbb; }
.audit-log__card--secret {
    border-color: #7a3db1;
    background: #f1eafb;
}
.audit-log__card--secret:hover { border-color: #642ba0; }
.audit-log__card--hidden {
    border-color: #000;
    background: #e9e9ec;
}
.audit-log__card--hidden:hover { border-color: #000; }

/* New cards arriving via the live feed fade + slide in (TODO/0 #26). */
@keyframes audit-card-in {
    from { opacity: 0; transform: translateY(-0.5rem); }
    to   { opacity: 1; transform: translateY(0); }
}
.audit-log__card--new {
    animation: audit-card-in 0.4s ease both;
}
@media (prefers-reduced-motion: reduce) {
    .audit-log__card--new { animation: none; }
}

.audit-log__avatar {
    width: 2.6rem;
    height: 2.6rem;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
    background: var(--color-2-darker);
    border: 1px solid #d8d2c5;
}

.audit-log__body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

.audit-log__heading {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.75rem;
}

.audit-log__name {
    font-weight: bold;
    color: var(--color-1);
    font-size: 0.95rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.audit-log__user {
    font-weight: normal;
    color: var(--color-4-lighter);
    font-size: 0.85rem;
    margin-left: 0.25rem;
}

.audit-log__time {
    font-size: 0.75rem;
    color: var(--color-4-lighter);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    flex-shrink: 0;
}

.audit-log__summary {
    font-size: 0.9rem;
    color: var(--color-4);
    line-height: 1.35;
    word-break: break-word;
}

.audit-log__pagination {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 1rem;
    padding-top: 0.5rem;
    border-top: 1px solid #e4ddd0;
}

.audit-log__page-indicator {
    font-size: 0.8rem;
    color: var(--color-4-lighter);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.audit-log__delete {
    margin-left: auto;
}

/* In the sideModal the available width is much narrower — let the heading
   wrap so the timestamp drops below the name instead of being clipped, and
   shrink the filter form so it doesn't dominate the panel. */
#global-modal .audit-log__heading {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.1rem;
}

#global-modal .audit-log__filters {
    padding: 0.6rem 0.7rem;
    margin-bottom: 0.6rem;
    font-size: 0.85rem;
}

#global-modal .audit-log__filters .admin-form__row {
    flex-direction: column;
    gap: 0.4rem;
}

/* ===================================================================
   Stat trace modal — rendered into #global-modal when a sheet stat is
   inspected. Document-style list: name on the left, delta on the right.
   =================================================================== */

.stat-trace {
    padding: 0 0.25rem;
}

.stat-trace__list {
    list-style: none;
    padding: 0;
    margin: 0;
}

.stat-trace__row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1rem;
    padding: 0.45rem 0.25rem;
    border-bottom: 1px solid #e4ddd0;
}

.stat-trace__row:last-child {
    border-bottom: none;
}

.stat-trace__label {
    color: var(--color-4);
    font-size: 0.9rem;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.stat-trace__value {
    font-variant-numeric: tabular-nums;
    color: var(--color-4-darkest);
    font-size: 0.9rem;
    white-space: nowrap;
    flex-shrink: 0;
}

.stat-trace__row--base .stat-trace__label {
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: 0.75rem;
    color: #6a5d44;
    font-weight: bold;
}

.stat-trace__row--final {
    margin-top: 0.4rem;
    padding-top: 0.6rem;
    border-top: 2px solid #d8d2c5;
}

.stat-trace__row--final .stat-trace__label {
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: 0.8rem;
    color: #6a5d44;
    font-weight: bold;
}

.stat-trace__row--final .stat-trace__value {
    font-size: 1.15rem;
    font-weight: bold;
    color: var(--color-1);
}

.stat-trace__row--muted .stat-trace__label,
.stat-trace__row--muted .stat-trace__value {
    color: var(--color-4-lighter);
    font-style: italic;
}

.stat-trace__row--error .stat-trace__label,
.stat-trace__row--error .stat-trace__value {
    color: #c0392b;
}

/* Manual stat overrides (TODO/0 #44): blue, matching the overridden-value flag
   on the characteristics boxes (#22) so a trace row reads as deliberately set. */
.stat-trace__row--override .stat-trace__label,
.stat-trace__row--override .stat-trace__value {
    color: #1d4ed8;
}

/* TODO/0 #7: manual stat overrides. Deliberately small and muted — a power-user
   affordance tucked under a collapsed "Customize" disclosure. */
.stat-trace__customize {
    margin-bottom: 0.75rem;
    border-bottom: 1px solid #e4ddd0;
    padding-bottom: 0.5rem;
}

.stat-trace__customize-summary {
    cursor: pointer;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--color-4-lighest);
    list-style: none;
    user-select: none;
}

.stat-trace__customize-summary::before {
    content: '▸';
    display: inline-block;
    margin-right: 0.35rem;
    font-size: 0.65rem;
    transition: transform 0.12s ease;
}

.stat-trace__customize[open] .stat-trace__customize-summary::before {
    transform: rotate(90deg);
}

.stat-override-form {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin-top: 0.6rem;
}

.stat-override-field {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.stat-override-field__label {
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-4-lighest);
}

.stat-override-field__hint {
    text-transform: none;
    letter-spacing: 0;
    font-style: italic;
    opacity: 0.7;
}

.stat-override-field__input {
    font-size: 0.78rem;
    padding: 0.2rem 0.4rem;
    border: 1px solid #d8d2c5;
    border-radius: 3px;
    background: #fbf9f4;
    color: var(--color-4);
    width: 100%;
}

.stat-override-field__input:focus {
    outline: none;
    border-color: var(--color-4-lighter);
    background: #fff;
}

.stat-override-form__apply {
    align-self: flex-end;
    font-size: 0.72rem;
    padding: 0.25rem 0.7rem;
    border: 1px solid #d8d2c5;
    border-radius: 3px;
    background: #f0ece3;
    color: var(--color-4);
    cursor: pointer;
}

.stat-override-form__apply:hover {
    background: #e6e0d4;
}

.stat-override-form__apply:disabled {
    opacity: 0.5;
    cursor: default;
}

#global-modal .audit-log__filters .admin-form__group {
    margin-bottom: 0;
}

#global-modal .audit-log__filters .admin-form__label {
    font-size: 0.7rem;
    margin-bottom: 0.2rem;
}

#global-modal .audit-log__filters .admin-form__input {
    padding: 0.35rem 0.5rem;
    font-size: 0.85rem;
    border-radius: 0.3rem;
}

#global-modal .audit-log__filters .admin-form__help {
    display: none;
}

#global-modal .audit-log__filters .admin-form__actions {
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: 0.6rem;
}

#global-modal .audit-log__filters .btn {
    padding: 0.4rem 0.8rem;
    font-size: 0.8rem;
}

#global-modal .audit-log__delete {
    margin-left: 0;
    width: 100%;
}

/* Tag filter row on the notes list */
.admin-card-list__filter-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0;
    margin-bottom: 0.5rem;
}

.admin-card-list__filter-label {
    font-weight: bold;
    margin-right: 0.25rem;
}

.admin-card-list__filter-row .btn.is-active {
    background: var(--color-1);
    color: #fafafa;
    border-color: var(--color-1-darkest);
}

.admin-card-list__tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    margin-top: 0.25rem;
}

/* ---------------------------------------------------------------------------
   Notes FAB — quick-note popover anchored to the FAB
--------------------------------------------------------------------------- */
.notes-fab__popover {
    position: absolute;
    top: calc(100% + 0.5rem);
    left: 0;
    width: 22rem;
    height: 22rem;
    min-width: 18rem;
    min-height: 16rem;
    max-width: calc(100vw - 2rem);
    max-height: calc(100vh - 4rem);
    background: #fffcfc;
    border: 1.5px solid var(--color-1-lighter);
    border-radius: 0.6rem;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.22);
    padding: 0.75rem;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    resize: both;
    overflow: hidden;
}

.notes-fab__popover[hidden] {
    display: none;
}

.notes-fab__popover-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 0.95rem;
}

.notes-fab__popover-close {
    background: none;
    border: none;
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
    color: var(--color-1);
    padding: 0 0.25rem;
}

.notes-fab__popover-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
}

.notes-fab__popover-error {
    color: #c0392b;
    font-size: 0.85rem;
}

/* Let the embedded TipTap editor fill the resizable popover, so dragging the
   resize handle grows the editor surface (not just whitespace). */
.notes-fab__popover .tiptap-editor {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.notes-fab__popover .tiptap-editor__surface {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
    overflow-y: auto;
}

/* Make the ProseMirror editable fill the surface so its clickable area
   (and the visible editing region) grows with the popover. */
.notes-fab__popover .tiptap-editor__surface .ProseMirror {
    min-height: 100%;
    height: 100%;
    box-sizing: border-box;
}

/* Toast stack — bottom-left so it doesn't overlap the FAB. */
.notes-toast-stack {
    position: fixed;
    left: 1.25rem;
    bottom: 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    z-index: 90;
    pointer-events: none;
}

.notes-toast {
    background: var(--color-1);
    color: #fafafa;
    padding: 0.6rem 0.9rem;
    border-radius: 0.4rem;
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
    display: flex;
    align-items: center;
    gap: 0.5rem;
    pointer-events: auto;
    transition: opacity 0.3s ease, transform 0.3s ease;
}

.notes-toast--leaving {
    opacity: 0;
    transform: translateY(0.5rem);
}

.notes-toast__link {
    color: #fafafa;
    text-decoration: underline;
    font-weight: bold;
}

.notes-toast__close {
    background: none;
    border: none;
    color: #fafafa;
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
    padding: 0 0.25rem;
}

/* ---------------------------------------------------------------------------
   Handout authoring — decodeWeight mark visual hint (editor surface only).
   Player-facing output strips the mark, so this never leaks to readers.
   --w is set inline by the mark's renderHTML; falls back to 1 if absent.
--------------------------------------------------------------------------- */
.tiptap-editor [data-decode-weight] {
    background: hsl(calc(120 - var(--w, 1) * 90), 70%, 92%);
    border-bottom: 1px dashed currentColor;
    border-radius: 2px;
    padding: 0 1px;
}

.tiptap-editor__decode-label {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.85rem;
    color: var(--color-4-darkest);
}

.tiptap-editor__decode-input {
    width: 4.5rem;
    padding: 0.15rem 0.35rem;
    font-size: 0.85rem;
    border: 1px solid var(--color-2-darkest);
    border-radius: 0.25rem;
}

/* ---------------------------------------------------------------------------
   Recipient picker (handout publish panel)
--------------------------------------------------------------------------- */
.recipient-picker {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.recipient-picker__chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    min-height: 1.5rem;
}
.recipient-picker__chip {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.15rem 0.5rem;
    background: var(--color-2);
    border: 1px solid var(--color-2-darkest);
    border-radius: 999px;
    font-size: 0.85rem;
}
.recipient-picker__chip-remove {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    padding: 0 0.15rem;
    color: var(--color-1);
}
.recipient-picker__search {
    position: relative;
}
.recipient-picker__suggestions {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    list-style: none;
    margin: 0;
    padding: 0.25rem 0;
    background: #fffcfc;
    border: 1px solid var(--color-2-darkest);
    border-radius: 0.3rem;
    box-shadow: 0 6px 16px rgba(0,0,0,0.18);
    max-height: 12rem;
    overflow-y: auto;
    z-index: 5;
}
.recipient-picker__suggestion {
    padding: 0.4rem 0.75rem;
    cursor: pointer;
}
.recipient-picker__suggestion:hover {
    background: var(--color-2-light);
}

/* ---------------------------------------------------------------------------
   Player-facing handout body — coded vs decoded text styling.
   Substituted conlang text is wrapped in <span class="conlang-text"> by the
   cipher transform; decoded plaintext is rendered without that wrapper, so
   the cascade makes plaintext stand out by contrast.
--------------------------------------------------------------------------- */

.handout-body :not(.conlang-text) {
    /* Plaintext (revealed) — bumped weight and full-strength colour. */
    font-weight: bold;
}

.handout-body .conlang-text {
    font-style: italic;
    font-family: 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, serif;
    letter-spacing: 0.04em;
    color: var(--color-4-darkest, #555);
    opacity: 0.78;
}



/* Direct text children of paragraphs/headings/list items can't be selected
   via :not() above; this rule isn't load-bearing but anchors intent. */
.handout-body {
    color: var(--color-4-darkest, #222);
    line-height: 1.6;
}

/* Admin note form: side-by-side controls + recipient preview. */
.handout-preview-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 1.5rem;
    align-items: start;
}
.handout-preview-layout__controls > * + * {
    margin-top: 1rem;
}
.handout-preview-layout__preview {
    position: sticky;
    top: 1rem;
    background: var(--color-2-light, #fffdf8);
    border: 1px solid var(--color-3, #d8d2c5);
    border-radius: 0.5rem;
    padding: 1rem;
    max-height: 70vh;
    overflow-y: auto;
}
.handout-preview-pane__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--color-3, #e4ddd0);
}
.handout-preview-pane__body {
    min-height: 4rem;
}
@media (max-width: 60rem) {
    .handout-preview-layout {
        grid-template-columns: 1fr;
    }
    .handout-preview-layout__preview {
        position: static;
        max-height: none;
    }
}

/* ── Character Career Picker ─────────────────────────────────────────── */
.character-careers { margin-top: 1.5rem; }
.character-careers__help { font-size: 0.9em; color: #555; margin: 0.25rem 0 0.75rem; }
.character-career-block {
    border: 1px solid #ccc; border-radius: 4px; padding: 0.75rem;
    margin-bottom: 0.75rem; background: #fafafa;
}
.character-career-block__head { display: flex; gap: 0.5rem; align-items: center; }
.character-career-block__body { margin-top: 0.5rem; }
.character-career-block__rank-row { display: flex; gap: 0.5rem; align-items: center; margin-bottom: 0.5rem; }
.character-career-block__skill-area { display: flex; flex-direction: column; gap: 0.25rem; }
.character-career-block__skill-row { display: flex; gap: 0.5rem; align-items: flex-start; }

/* #45b: a skill row holds one or more (specialisation, points) entries. */
.character-career-block__entries { display: flex; flex-direction: column; gap: 0.25rem; flex: 1; }
.character-career-block__entry { display: flex; gap: 0.4rem; align-items: center; }
.character-career-block__entry-spec { flex: 0 0 9rem; width: 9rem; }
/* Points input must keep a usable width even next to the spec field. */
.character-career-block__entry input[type="number"] { flex: 0 0 4.5rem; width: 4.5rem; }
.character-career-block__entry-spec-label { font-style: italic; color: var(--color-4-lighter); }
.character-career-block__add-spec { align-self: flex-start; }
.character-career-block__entry-remove { flex: 0 0 auto; padding: 0 0.45rem; line-height: 1.4; }
.character-career-block__or-label {
    font-size: 0.75em; font-weight: bold; color: #888;
    background: #eee; padding: 0.1rem 0.4rem; border-radius: 3px;
}
.character-career-block__ability-area { display: flex; flex-direction: column; gap: 0.5rem; }
.character-career-block__rank {
    border-left: 3px solid #999; padding: 0.25rem 0.5rem; background: #fff;
}
.character-career-block__xor {
    margin-left: 0.5rem; padding: 0.25rem; border: 1px dashed #bbb; border-radius: 3px;
}
.character-career-block__xor-label { font-size: 0.85em; font-weight: bold; color: #555; }

/* ── Character Editor (modern) ───────────────────────────────────────── */
.character-editor__identity {
    display: flex; gap: 1.5rem; align-items: flex-start; margin-bottom: 0.5rem;
}
.character-editor__avatar { flex: 0 0 auto; text-align: center; }
.character-editor__avatar-img {
    max-width: 200px; max-height: 200px;
    border: 2px solid var(--color-4, #ccc); border-radius: 4px;
    cursor: pointer; display: block;
}
.character-editor__avatar-hint {
    font-size: 0.75em; color: #777; margin: 0.25rem 0 0; max-width: 200px;
}
.character-editor__identity-fields { flex: 1; }
.character-editor__race-details,
.character-editor__culture-details { margin-top: 0.5rem; }
.character-editor__race-details > div,
.character-editor__culture-details > div {
    margin-bottom: 0.5rem;
}
.character-editor__race-details label,
.character-editor__culture-details label {
    display: inline-block; min-width: 8rem;
    font-weight: 600; color: var(--color-4-darkest, #333);
}
.character-editor__race-details input[type="number"],
.character-editor__culture-details input[type="number"] {
    padding: 0.25rem 0.5rem; border: 1px solid #ccc; border-radius: 3px;
    width: 5rem;
}
#pointsWrapper, #culturePointsWrapper {
    background: var(--color-2-light, #eef);
    border-left: 3px solid var(--color-2, #66c);
    padding: 0.4rem 0.75rem; margin-bottom: 0.5rem; border-radius: 3px;
}
.character-editor__culture-details h3 {
    font-size: 1em; margin: 0.75rem 0 0.25rem; color: var(--color-4-darker, #444);
}
/* #45b: culture skill entries (fixed/non-spec rows, and (Any) freeform blocks). */
.character-editor__culture-skill-row { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.35rem; }
.character-editor__culture-skill-spec { padding: 0.2rem 0.45rem; border: 1px solid #ccc; border-radius: 3px; width: 9rem; }
.character-editor__culture-any { margin-bottom: 0.5rem; }
.character-editor__culture-add-spec { margin-top: 0.15rem; }
.character-editor__careers-locked {
    color: #777; font-size: 0.9em; margin: 0.5rem 0 1rem;
}

/* ── Character editor: characteristics + derived-attribute preview (TODO/0 #14) ── */
.char-stats { margin-top: 0.5rem; }
.char-stats__head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem; margin-bottom: 0.85rem;
    border-bottom: 1px solid #e4ddd0; padding-bottom: 0.45rem;
}
.char-stats__title,
.char-preview__title {
    margin: 0; font-size: 0.95rem; text-transform: uppercase;
    letter-spacing: 0.08em; color: #6a5d44; font-weight: 700;
}
.char-points {
    display: inline-flex; align-items: baseline; gap: 0.35rem;
    background: #f4efe4; border: 1px solid #d8cfba; border-radius: 999px;
    padding: 0.2rem 0.75rem; white-space: nowrap;
}
.char-points__num { font-size: 1.05rem; font-weight: 700; color: #6a5d44; }
.char-points--over .char-points__num { color: #b22222; }
.char-points__label {
    font-size: 0.7rem; color: #8a7d64; text-transform: uppercase; letter-spacing: 0.04em;
}

.char-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr));
    gap: 0.75rem;
}
.char-field {
    display: flex; flex-direction: column; align-items: center; gap: 0.35rem;
    padding: 0.6rem 0.4rem; background: #fffdf8;
    border: 1px solid #e4ddd0; border-radius: 0.6rem;
}
.char-field__label {
    font-size: 0.78rem; font-weight: 600; color: #4a4030;
    text-align: center; line-height: 1.1; max-width: 100%;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.char-field__control { display: inline-flex; align-items: stretch; }
.char-field__btn {
    border: 1px solid #d8cfba; background: #f4efe4; color: #6a5d44;
    width: 1.6rem; cursor: pointer; font-size: 1rem; line-height: 1;
    display: flex; align-items: center; justify-content: center; user-select: none;
}
.char-field__btn:first-child { border-radius: 0.4rem 0 0 0.4rem; }
.char-field__btn:last-child  { border-radius: 0 0.4rem 0.4rem 0; }
.char-field__btn:hover { background: #6a5d44; color: #fff; border-color: #6a5d44; }
/* 3 classes to outrank the legacy .character-editor__race-details input rule. */
.character-editor__race-details .char-field .char-field__input {
    width: 2.7rem; text-align: center; font-size: 1.1rem; font-weight: 700;
    color: #2f2a20; padding: 0.25rem 0; margin: 0;
    border: 1px solid #d8cfba; border-left: none; border-right: none; border-radius: 0;
    -moz-appearance: textfield;
}
.char-field__input:focus { outline: none; background: #fffaf0; }
.char-field__input::-webkit-outer-spin-button,
.char-field__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.char-field__notation { font-size: 0.68rem; color: #9a8f7c; }

.char-preview { margin-top: 1.1rem; }
.char-preview__hint {
    font-size: 0.7rem; font-weight: 400; text-transform: none;
    letter-spacing: 0; color: #9a8f7c;
}
.char-preview__grid {
    list-style: none; margin: 0.6rem 0 0; padding: 0;
    display: grid; grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr));
    gap: 0.5rem;
}
.char-preview__item {
    display: flex; flex-direction: column; align-items: center; gap: 0.15rem;
    padding: 0.5rem 0.3rem; background: #f7f3ea;
    border: 1px dashed #d8cfba; border-radius: 0.5rem;
}
.char-preview__value { font-size: 1.05rem; font-weight: 700; color: #6a5d44; line-height: 1; }
.char-preview__label {
    font-size: 0.68rem; color: #8a7d64; text-align: center; line-height: 1.1;
    text-transform: uppercase; letter-spacing: 0.03em;
}

/* Career picker — granted/auto-awarded abilities (read-only) */
.character-career-block__granted {
    margin-bottom: 0.4rem; padding: 0.3rem 0.5rem;
    background: #f0f7f0; border-left: 3px solid #6a8;
    border-radius: 3px;
}
.character-career-block__granted-label {
    font-size: 0.75em; font-weight: bold; color: #4a6; text-transform: uppercase;
    letter-spacing: 0.05em;
}
.character-career-block__granted-list {
    margin: 0.15rem 0 0 1.25rem; padding: 0; font-size: 0.9em;
}
.character-career-block__abilities-empty {
    color: #888; font-style: italic; font-size: 0.9em; margin: 0.25rem 0;
}

/* Career picker — skill subsections (Standard vs Professional) */
.character-career-block__skill-subhead {
    margin: 0.6rem 0 0.25rem; font-size: 0.95em; color: var(--color-4-darker, #444);
    border-bottom: 1px solid #ddd; padding-bottom: 0.2rem;
}
.character-career-block__prof-counter {
    font-size: 0.85em; font-weight: normal; color: #666; margin-left: 0.4rem;
}
.character-career-block__prof-counter--over { color: #c33; font-weight: bold; }
.character-career-block__prof-hint {
    font-size: 0.8em; color: #777; margin: 0 0 0.4rem; font-style: italic;
}

.character-career-block__pool-counter {
    font-size: 0.8em; font-weight: normal; color: #666; margin-left: 0.4rem;
}
.character-career-block__pool-counter--over { color: #c33; font-weight: bold; }

.character-career-block__skill-total {
    margin-left: 0.5rem; font-weight: 700; color: var(--color-3, #FF7F11);
    min-width: 3rem; text-align: right; font-variant-numeric: tabular-nums;
}

/* Career picker — prerequisite check */
.character-career-block__prereq-banner {
    margin: 0.4rem 0; padding: 0.35rem 0.6rem; border-radius: 3px;
    font-size: 0.9em; font-weight: 600;
}
.character-career-block__prereq-banner:empty { display: none; }
.character-career-block__prereq-banner--ok {
    background: #e8f5e9; color: #2e7d32; border-left: 3px solid #2e7d32;
}
.character-career-block__prereq-banner--fail {
    background: #ffebee; color: #c62828; border-left: 3px solid #c62828;
}
.character-career-block__skill-row--prereq-fail {
    background: #ffebee; border-radius: 3px; padding: 0.15rem 0.3rem;
}
.character-career-block__skill-row--prereq-fail .admin-form__input { border-color: #c62828; }

.character-career-block__xor-desc {
    margin-left: 1.5rem; font-size: 0.85em; color: #555;
    font-style: italic; line-height: 1.3;
}

/* ── Bonus Skill Points ──────────────────────────────────────────────── */
.character-bonus-points { margin-top: 0.5rem; }
.character-bonus-points__head {
    display: flex; gap: 0.6rem; align-items: center; margin-bottom: 0.5rem;
}
.character-bonus-points__counter {
    font-size: 0.9em; color: #666; font-variant-numeric: tabular-nums;
}
.character-bonus-points__counter--over { color: #c33; font-weight: bold; }
.character-bonus-points__subhead {
    margin: 0.6rem 0 0.25rem; font-size: 0.95em; color: var(--color-4-darker, #444);
    border-bottom: 1px solid #ddd; padding-bottom: 0.2rem;
}
.character-bonus-points__row {
    display: flex; gap: 0.5rem; align-items: center; margin-bottom: 0.2rem;
}
.character-bonus-points__name { flex: 1; }
.character-bonus-points__points { width: 5rem; text-align: right; }
.character-bonus-points__name--prereq strong { color: var(--color-3, #FF7F11); }

.character-bonus-points__total,
.character-editor__culture-skill-total {
    margin-left: 0.5rem; font-weight: 700; color: var(--color-3, #FF7F11);
    min-width: 3rem; text-align: right; font-variant-numeric: tabular-nums;
}

/* ── Character Editor Header + Tabs ──────────────────────────────────── */
.character-editor__header {
    display: flex; gap: 1.5rem; align-items: center;
    padding: 1rem; margin-bottom: 1rem;
    border: 1px solid var(--color-4, #ccc); border-radius: 6px;
    background: var(--color-2-lighter, #fafafa);
    position: sticky; top: 0; z-index: 10;
}
.character-editor__header .character-editor__avatar { flex: 0 0 auto; }
.character-editor__header .character-editor__avatar-img {
    max-width: 120px; max-height: 120px;
}
.character-editor__header-fields { flex: 1; display: flex; flex-direction: column; gap: 0.25rem; }
.character-editor__name-input { font-size: 1.2em; font-weight: 600; }
.character-editor__avatar-hint {
    font-size: 0.7em; color: #777; margin: 0.15rem 0 0; max-width: 120px;
}
.character-editor__tabs {
    display: flex; gap: 0; margin-bottom: 1rem;
    border-bottom: 2px solid var(--color-4, #888);
}

/* View-sheet shortcut in the editor tab row (TODO/0 #15). The notebook SVG is
   white-filled, so it's used as a mask and tinted via background-color. */
.character-editor__sheet-link {
    margin-left:1rem;
    align-self: center;
    width: 1.6rem;
    height: 1.6rem;
    cursor: pointer;
    background-color: #6a5d44;
    -webkit-mask: url('/images/icons/icon-notebook.svg') center / contain no-repeat;
    mask: url('/images/icons/icon-notebook.svg') center / contain no-repeat;
}
.character-editor__sheet-link:hover { background-color: var(--color-3, #FF7F11); }
.character-editor__tab-btn {
    padding: 0.6rem 1.2rem; border: none; background: transparent;
    font-size: 0.95em; font-weight: 600; color: var(--color-4-lightest, #666);
    cursor: pointer; border-bottom: 3px solid transparent;
    margin-bottom: -2px;
}
.character-editor__tab-btn:hover { color: var(--color-3, #FF7F11); }
.character-editor__tab-btn--active {
    color: var(--color-3, #FF7F11);
    border-bottom-color: var(--color-3, #FF7F11);
}
.character-editor__tab { display: none; }
.character-editor__tab--active { display: block; }

/* Fix the character editor wrapper to viewport size so content scrolls
   internally rather than growing the page. The sticky header pins to the
   top of the wrapper's scroll area. */
.admin-create-wrapper.character-editor {
    width: 100%;
    box-sizing: border-box;
    overflow: visible;
}
/* Inactive tabs stay in the DOM (display:none) but their content widths can
   still influence the wrapper's intrinsic width. Force each tab to fill the
   wrapper so the wrapper doesn't shrink-wrap to the widest tab. */
.character-editor__tab { width: 100%; box-sizing: border-box; }
.character-editor__tab > * { max-width: 100%; }

/* ============================================================
   Loot pools (TODO #12) — admin list filter, badges, entry tables
   ============================================================ */
.admin-list-filter {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    margin: 0.75rem 0 1rem;
    flex-wrap: wrap;
}
.admin-list-filter .admin-form__input { width: auto; flex: 0 1 16rem; }
.admin-list-filter select.admin-form__input { flex: 0 0 auto; }

.badge {
    display: inline-block;
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    padding: 0.1rem 0.5rem;
    border-radius: 0.7rem;
    vertical-align: middle;
}
.badge--neutral { background: var(--color-2-darker); color: var(--color-4); }
.badge--warning { background: var(--color-3); color: #fff; }

.loot-entries, .loot-currency-section { margin-top: 1.75rem; }
.loot-entries h3, .loot-currency-section h3 {
    color: var(--color-1);
    border-bottom: 2px solid var(--color-2-darker);
    padding-bottom: 0.3rem;
}

/* Item search (autocomplete) */
.loot-item-search { position: relative; max-width: 28rem; margin-bottom: 1rem; }
.loot-item-search__results {
    position: absolute;
    z-index: 30;
    left: 0; right: 0;
    margin: 0.15rem 0 0;
    padding: 0;
    list-style: none;
    background: var(--color-2);
    border: 1px solid var(--color-2-darkest);
    border-radius: 0.5rem;
    box-shadow: 0 4px 14px rgba(0,0,0,0.16);
    max-height: 16rem;
    overflow-y: auto;
}
.loot-item-search__opt { padding: 0.4rem 0.7rem; cursor: pointer; }
.loot-item-search__opt:hover { background: var(--color-1); color: var(--color-2); }

/* Item cards */
.loot-cards { display: flex; flex-direction: column; gap: 0.6rem; }
.loot-card {
    background: var(--color-2);
    border: 1px solid #dadada;
    border-radius: 0.6rem;
    padding: 0.7rem 0.9rem;
    box-shadow: 0 1px 3px rgba(0,0,0,0.06);
}
.loot-card__head { display: flex; align-items: center; gap: 0.5rem; }
.loot-card__name { font-weight: 700; color: var(--color-1); }
.loot-card__remove { margin-left: auto; }
.loot-card__fields {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    margin-top: 0.6rem;
}
.loot-field { display: flex; flex-direction: column; gap: 0.15rem; flex: 1 1 12rem; }
.loot-field--sm { flex: 0 1 7rem; }
.loot-field > span {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-4-lighter);
}
.loot-card .admin-form__input { padding: 0.3rem 0.45rem; }

/* Currency rows */
.loot-coins { display: flex; flex-direction: column; gap: 0.4rem; max-width: 28rem; }
.loot-coin { display: flex; align-items: center; gap: 0.6rem; }
.loot-coin__name { flex: 0 0 9rem; font-weight: 600; color: var(--color-4); }
.loot-coin .admin-form__input { flex: 1; padding: 0.3rem 0.45rem; }

.loot-saved {
    font-size: 0.75rem;
    color: #2e8b3d;
    margin-right: 0.4rem;
}

/* Loot transfer page (split board) */
.loot-transfer__head { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.loot-transfer__edit { margin-left: auto; }
.loot-transfer__campaign { max-width: 24rem; }

.loot-transfer__board {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 1.25rem;
    margin-top: 0.5rem;
}
@media (max-width: 60rem) { .loot-transfer__board { grid-template-columns: 1fr; } }

.loot-transfer__loot, .loot-transfer__recipient {
    background: var(--color-2);
    border: 1px solid #dadada;
    border-radius: 0.6rem;
    padding: 0.8rem 1rem;
}
.loot-transfer__loot h3, .loot-transfer__recipient h3 { margin-top: 0; color: var(--color-1); }
.lt-subhead { margin: 0.9rem 0 0.3rem; color: var(--color-4-lighter); font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.03em; }

.lt-list { list-style: none; margin: 0; padding: 0; }
.lt-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.35rem 0;
    border-bottom: 1px solid var(--color-2-darker);
}
.lt-row__name { flex: 1; font-weight: 600; color: var(--color-4); }
.lt-row__detail { font-weight: 400; font-size: 0.85rem; color: var(--color-4-lighest); }
.lt-row__qty { display: flex; align-items: center; gap: 0.4rem; }
.lt-qty { width: 5rem; padding: 0.25rem 0.4rem; text-align: right; }
.lt-roll { white-space: nowrap; }

.lt-recipient { display: flex; align-items: center; gap: 0.5rem; padding: 0.35rem 0.2rem; cursor: pointer; }
.lt-recipient:hover { background: var(--color-2-lighter); border-radius: 0.4rem; }

.loot-transfer__actions { align-items: center; gap: 0.8rem; }
.loot-transfer__result { font-size: 0.9rem; }
.loot-transfer__result--ok { color: #2e8b3d; }
.loot-transfer__result--err { color: var(--color-1); font-weight: 600; }

/*--- Tom Select (relation pickers) — theme to match .admin-form__input ---*/
/* Tom Select copies the source <select>'s classes onto .ts-wrapper; strip the
   input chrome off the wrapper so it isn't drawn twice, and put it on .ts-control. */
.ts-wrapper.admin-form__input {
    padding: 0;
    border: none;
    background: transparent;
    height: auto;
}
.ts-wrapper .ts-control {
    padding: 0.45rem 0.6rem;
    font-size: 1rem;
    font-family: inherit;
    color: var(--color-4-darkest);
    background: #fff;
    border: 1.5px solid var(--color-2-darkest);
    border-radius: 0.4rem;
    box-shadow: none;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.ts-wrapper.focus .ts-control {
    border-color: var(--color-1);
    box-shadow: 0 0 0 3px rgba(78, 25, 25, 0.12);
}
/* Selected chips (the "many" tokens). */
.ts-wrapper.multi .ts-control > .item {
    background: var(--color-1);
    color: var(--color-2);
    border: 1px solid var(--color-1-darker);
    border-radius: 0.3rem;
    padding: 0.1rem 0.4rem;
}
.ts-wrapper.multi .ts-control > .item .remove {
    border-left-color: var(--color-1-lighter);
    color: var(--color-2);
}
.ts-wrapper.multi .ts-control > .item .remove:hover {
    background: var(--color-1-darker);
}
/* Dropdown menu + highlighted option. */
.ts-dropdown {
    border: 1.5px solid var(--color-2-darkest);
    border-radius: 0.4rem;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
}
.ts-dropdown .active {
    background: var(--color-21);
    color: var(--color-1);
}

/*--- Image field (article-form upload component) ---*/
.image-field { display: flex; flex-direction: column; gap: 0.6rem; }
.image-field__list { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.image-field__list:empty { display: none; }

.image-field__item {
    position: relative;
    width: 7rem;
    height: 7rem;
    border: 1.5px solid var(--color-2-darkest);
    border-radius: 0.4rem;
    overflow: hidden;
    background: var(--color-2-lighter);
}
.image-field__item[draggable="true"] { cursor: grab; }
.image-field__item--dragging { opacity: 0.5; }
.image-field__thumb { width: 100%; height: 100%; object-fit: cover; display: block; }

.image-field__remove {
    position: absolute;
    top: 0.25rem;
    right: 0.25rem;
    width: 1.4rem;
    height: 1.4rem;
    line-height: 1;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.6);
    color: #fff;
    font-size: 1rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}
.image-field__remove:hover { background: var(--color-1); }

.image-field__drop {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    border: 1.5px dashed var(--color-2-darkest);
    border-radius: 0.4rem;
    color: var(--color-4-darkest);
    background: #fff;
    cursor: pointer;
    text-align: center;
    transition: border-color 0.15s ease, background 0.15s ease;
}
.image-field__drop:hover { border-color: var(--color-1); }
.image-field__drop--over {
    border-color: var(--color-1);
    background: var(--color-21);
}
.image-field--busy { opacity: 0.6; pointer-events: none; }

/* Header field: a section heading spanning both infobox columns. */
.article-infobox__section { padding-top: 0.6rem; }
.article-infobox__section h3 {
    margin: 0;
    padding-bottom: 0.25rem;
    /*border-bottom: 1.5px solid var(--color-1-lighter);*/
    text-align: center;
    font-size: 0.9rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-1);
}

/* Label-less field: value spans both columns, centred. */
.article-infobox__full { text-align: center; }
.article-infobox__full .article-infobox__images { justify-content: center; }

/*--- Article infobox images ---*/
.article-infobox__images { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.15rem; }
.article-infobox__images img { border-radius: 0.4rem; display: block; }
/* Single image: fill the infobox column. Gallery: uniform square thumbs. */
.article-infobox__images:not(.article-infobox__images--gallery) img { width: 100%; height: auto; }
.article-infobox__images--gallery img { width: 4.5rem; height: 4.5rem; object-fit: cover; }

/* --- Events (slice 0016) --------------------------------------------------
   The palette is ten tokens; the DB stores only an index into them, so the
   colours can be retuned here without a migration. `none` is the fallback for
   an uncategorised event — a default, not an eleventh choice. */
:root {
	--event-color-0: #B3352E;
	--event-color-1: #C8791B;
	--event-color-2: #A8901A;
	--event-color-3: #6B8E23;
	--event-color-4: #2E7D5B;
	--event-color-5: #1F7A8C;
	--event-color-6: #3E5C9A;
	--event-color-7: #6B4E9B;
	--event-color-8: #8E3A62;
	--event-color-9: #6E6259;
	--event-color-none: #B0A79E;
}

.event-dot {
	display: inline-block; width: 0.7rem; height: 0.7rem; margin-right: 0.45rem;
	border-radius: 50%; background: var(--event-color, var(--event-color-none));
	vertical-align: middle; flex: 0 0 auto;
}

.event-swatches { display: inline-flex; gap: 0.3rem; align-items: center; }
.event-swatch {
	position: relative; width: 1.25rem; height: 1.25rem; border-radius: 50%;
	background: var(--event-color); cursor: pointer;
	border: 2px solid transparent; box-sizing: content-box;
}
.event-swatch input { position: absolute; inset: 0; opacity: 0; cursor: pointer; margin: 0; }
/* :has lets the swatch show selection without a wrapper element; the outline
   fallback below keeps it keyboard-visible in engines without :has. */
.event-swatch:has(input:checked) { border-color: var(--color-4-darkest); }
.event-swatch:has(input:focus-visible) { outline: 2px solid var(--color-3); outline-offset: 2px; }
/* "No colour" — an empty well with a slash, for the pickers where a colour is
   optional (slice 0035: an article type with none falls back to a derived one). */
.event-swatch--none {
	background:
		linear-gradient(to top right,
			transparent calc(50% - 1px), var(--color-4-lighter, #7A7676) calc(50% - 1px),
			var(--color-4-lighter, #7A7676) calc(50% + 1px), transparent calc(50% + 1px)),
		var(--color-2, #F5F2F2);
	box-shadow: inset 0 0 0 1px var(--color-2-darker, #DCD9D9);
}

.event-category-form {
	display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; flex: 1;
}
.event-category-form .admin-form__input { flex: 0 1 16rem; }

.event-date { border: 1px solid var(--color-31); border-radius: 4px; padding: 0.6rem 0.8rem; margin: 0 0 1rem; }
.event-date__legend { font-size: 0.85rem; font-weight: 700; padding: 0 0.35rem; }
.event-date__hint { font-weight: 400; color: var(--color-4-lighter); }
.event-date__row { display: flex; gap: 0.6rem; flex-wrap: wrap; }
.event-date__field { display: flex; flex-direction: column; font-size: 0.8rem; gap: 0.2rem; }
.event-date__field .admin-form__input { width: 6rem; }
.event-date__field--month .admin-form__input { width: 10rem; }

/*---------------------------------------------Dashboard---------------------------------------------
   Layout adapted from context/mockup.html (a sidebar + widget-grid mockup). The
   mockup's own "Classical" gold/serif tokens are dropped in favour of the app's
   identity: local --d-* vars below alias the shared palette, so the whole board
   recolours from one place. Data is still placeholder (see HomeController). */
.dashboard {
	--d-accent:        var(--color-1);          /* Night Bordeaux — brand */
	--d-accent-strong: var(--color-1-lighter);
	--d-accent-soft:   var(--color-21);         /* Champagne Mist — chips/tags */
	--d-highlight:     var(--color-3);          /* Vivid Tangerine — sparingly */
	--d-surface:       var(--color-2);          /* White Smoke — sidebar/cards */
	--d-text:          var(--color-5);          /* Carbon Black */
	--d-muted:         var(--color-4-lighter);
	--d-divider:       var(--color-31);         /* Khaki Beige */

	/* Persistent-topbar height — shared so the sticky editor toolbar can offset
	   below it (both stick within #dashboard__main) rather than overlapping. */
	--topbar-height: 4.25rem;

	/* Floating New Article button (slice 0044). Shared so the main column can
	   reserve room for it instead of letting content scroll underneath. */
	--fab: 56px;

	/* One tap size for every square control in the shell -- hamburger, collapsed
	   search, the details panel's close button. Declared once so they cannot
	   drift apart, which is what happened when each sized itself: 44/38.5/30px. */
	--tap: 44px;

	/* Width of the article details panel. Shared, because the tab has to travel
	   EXACTLY this far to stay attached to the panel's edge -- two copies of the
	   number would drift the tab off the folder. */
	--details-w: min(88vw, 26rem);

	/* Break out of the centered `.container` flow — the board wants the frame. */
	width: 100%;
	align-self: stretch;
	display: flex;
	color: var(--d-text);
	/* Fill the space between the title bar and footer without a fragile
	   `calc(100vh - …)` that must exactly match their heights (any mismatch
	   overflowed 100vh → scrollbar + white line). */
	flex: 1 1 auto;
	min-height: 0;
}

/* Shared lucide-style icon: strokes follow currentColor. */
.dash-icon { width: 18px; height: 18px; flex: none; stroke: currentColor; fill: none;
	stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.dash-icon--sm { width: 14px; height: 14px; }
.dash-icon--accent { color: var(--d-accent); }
.dash-icon--muted { color: var(--d-muted); }

/* --- Sidebar --- */
.dashboard__sidebar {
	width: 264px; flex: none;
	display: flex; flex-direction: column;
	background: var(--d-surface);
	border-right: 1px solid var(--d-divider);
}
.dashboard__brand {
	display: flex; align-items: center; gap: 0.6rem;
	padding: 1.25rem 1.25rem 1rem;
	border-bottom: 1px solid var(--d-divider);
}
.dashboard__brand .dash-icon { width: 22px; height: 22px; }
.dashboard__brand-name { font-size: 1.15rem; font-weight: 700; letter-spacing: -0.01em; }

.dashboard__section { padding: 1.1rem 0.85rem 0.5rem; }
.dashboard__eyebrow {
	font-size: 0.7rem; letter-spacing: 0.1em; text-transform: uppercase;
	color: var(--d-muted); padding: 0 0.5rem 0.5rem;
}
.dashboard__eyebrow--accent { color: var(--d-accent); padding: 0; margin-bottom: 0.25rem; }

.dashboard__worlds { display: flex; flex-direction: column; gap: 2px; }
.dash-world {
	display: flex; align-items: center; gap: 0.6rem;
	padding: 0.4rem 0.5rem; border-radius: 5px; cursor: pointer;
	font-size: 0.95rem;
}
.dash-world:hover { background: rgba(0, 0, 0, 0.05); }
.dash-world--current { font-weight: 600; }
.dash-world__dot {
	width: 8px; height: 8px; flex: none; border-radius: 50%;
	background: var(--d-accent);
}
.dash-world--current .dash-world__dot { background: var(--d-highlight); }
.dash-world__name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.dashboard__new-world { width: 100%; justify-content: flex-start; margin-top: 0.4rem; }

.dashboard__hr { height: 1px; background: var(--d-divider); margin: 0.5rem 1rem; }

.dashboard__nav { padding: 0.5rem 0.85rem; display: flex; flex-direction: column; gap: 2px; }
.dash-nav-item {
	display: flex; align-items: center; gap: 0.7rem;
	padding: 0.5rem 0.6rem; border-radius: 5px;
	color: var(--d-text); text-decoration: none; font-weight: 500;
}
.dash-nav-item:hover { background: rgba(0, 0, 0, 0.05); }
.dash-nav-item--active { background: var(--d-accent-soft); color: var(--d-accent); font-weight: 700; }
.dash-nav-item--active .dash-icon { color: var(--d-accent); }
.dash-nav-item__label { font-size: 0.95rem; }

.dashboard__spacer { flex: 1; }

.dashboard__account { border-top: 1px solid var(--d-divider); padding: 0.85rem; }
.dash-account { display: flex; align-items: center; gap: 0.7rem; padding: 0.4rem; border-radius: 6px; }
.dash-account__avatar {
	width: 34px; height: 34px; flex: none; border-radius: 50%;
	background: var(--d-accent-soft); color: var(--d-accent);
	display: flex; align-items: center; justify-content: center;
	font-weight: 700; font-size: 0.95rem;
}
.dash-account__body { flex: 1; min-width: 0; }
.dash-account__name { font-size: 0.9rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dash-account__plan { font-size: 0.75rem; color: var(--d-muted); }

/* --- Buttons (dashboard-local, outline style from the mockup) --- */
.dash-btn {
	display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
	cursor: pointer; font: inherit; font-weight: 700; font-size: 0.9rem;
	color: var(--d-text); background: transparent;
	border: 1px solid transparent; border-radius: 5px;
	padding: 0.45rem 0.8rem;
}
.dash-btn--primary { color: var(--d-accent); border-color: var(--d-accent); }
.dash-btn--primary:hover { background: var(--d-accent-soft); }
.dash-btn--ghost { color: var(--d-accent); padding: 0.4rem 0.5rem; }
.dash-btn--ghost:hover { background: rgba(0, 0, 0, 0.05); }

/* --- Main column --- */
/* Also the shell layout's <main>: override the base `main { justify-content:
   center }` (which would centre content on the block axis here) and let content
   flow from the top. */
.dashboard__main { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: flex-start; }
/* SPA busy dim — delayed, so a fast partial swap never flickers; only a genuinely
   slow fetch becomes visible (mirrors the timeline's busy pattern). */
.dashboard__main { transition: opacity 120ms linear 250ms; }
.dashboard__main.is-loading { opacity: 0.6; }
.dashboard__header {
	padding: 1.75rem 2.25rem 1.25rem;
	display: flex; align-items: flex-start; justify-content: space-between;
	gap: 1.25rem; flex-wrap: wrap;
}
.dashboard__welcome { font-size: 2rem; line-height: 1.15; color: var(--d-text); margin: 0; }
.dashboard__actions { display: flex; align-items: center; gap: 0.6rem; }
.dash-search {
	display: flex; align-items: center; gap: 0.5rem;
	border: 1px solid var(--d-divider); border-radius: 6px;
	padding: 0.45rem 0.75rem; min-width: 220px;
}
.dash-search__placeholder { font-size: 0.9rem; color: var(--d-muted); }

/* --- Widget grid --- */
/* Dashboard grid (slice 0024) — 12 columns, widgets placed from the saved
   layout via --dw-x/--dw-y/--dw-w/--dw-h. Rows are `minmax(cell, auto)`, so a
   widget whose content outgrows its declared height GROWS THE ROW BAND rather
   than clipping; every card sharing that band grows with it, so the arrangement
   stays true to what was designed. A fixed row height would have looked tidier
   right up until someone wrote a long welcome note. */
.dashboard__grid {
	/* The three numbers edit mode has to agree with. `--dash-cell` and the gap
	   are read back by the grid component at init (it derives Gridstack's
	   cellHeight from them); `--dash-pad-x` is used by the edit-mode rule below.
	   Hard-coding any of it in the JS cannot work — the gap is in rem and the
	   root font-size is `clamp(13px, 1.6vw, 14px)`, so it is viewport-dependent. */
	--dash-cell: 84px;
	--dash-gap: 1.25rem;
	--dash-pad-x: 2.25rem;
	padding: 0 var(--dash-pad-x) 2.25rem;
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	grid-auto-rows: minmax(var(--dash-cell), auto);
	gap: var(--dash-gap);
}
.dash-widget {
	grid-column: calc(var(--dw-x, 0) + 1) / span var(--dw-w, 6);
	grid-row: calc(var(--dw-y, 0) + 1) / span var(--dw-h, 4);
	min-width: 0; /* or a long unbroken title stretches its column */
}
.dash-widget--full { grid-column: 1 / -1; }

.dash-card {
	background: var(--d-surface);
	border: 1px solid var(--d-divider); border-radius: 8px;
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
	overflow: hidden;
	/* ⚠️ A CONTAINER, so the card SHELL can respond to its own width. Every
	   widget's minimum is 1x1 (see DashboardRegistry MIN), which means a card can
	   be a twelfth of the grid — far narrower than any viewport breakpoint can
	   detect, so `@media` is the wrong tool and the slice-0044 three-token rule
	   does not apply. Safe here because nothing inside a dash card is absolutely
	   positioned, so the `contain: layout` this implies changes no containing
	   block. Widget BODIES are on their own; this only keeps the head from
	   overflowing. */
	container-type: inline-size; container-name: dash-card;
}
/* ⚠️ Fill the row band, do not shrink to content. `.dash-widget` is the grid
   item and stretches to the band on its own; this card is a static block inside
   it, so without a height it stops at its content and renders SHORTER than the
   height it was dragged to.

   ⚠️ And it must apply in read mode ONLY — hence `:not(.is-editing)` rather
   than a bare `.dash-card`. In edit mode this element IS Gridstack's
   `.grid-stack-item-content`, absolutely positioned with `top` AND `bottom`
   insets of half a gap. Giving it a height over-constrains the box (CSS 2.1
   §10.6.4: with top, height and bottom all set, `bottom` is ignored), so the
   card grows a whole gap taller than its grid item and closes up the vertical
   space to the card beneath it. The insets already size it there. */
.dashboard__grid:not(.is-editing) .dash-card { height: 100%; }
.dash-card__head {
	display: flex; align-items: center; justify-content: space-between;
	padding: 0.85rem 1.1rem; border-bottom: 1px solid var(--d-divider);
}
.dash-card__heading { display: flex; align-items: center; gap: 0.5rem; }
.dash-card__title { font-size: 1.1rem; font-weight: 700; }
.dash-card__link { font-size: 0.8rem; }
.dash-card__actions { display: flex; align-items: center; gap: 0.55rem; }
/* Editor-only, and quiet until wanted — a reader never sees it at all. */
.dash-card__cog { display: inline-flex; color: var(--d-muted); opacity: 0.65; }

/* ---------------------------------------------------------------------------
   Narrow cards — the shared shell only (every widget minimum is 1x1)
   ---------------------------------------------------------------------------
   Scoped to the container queries deliberately: the title wraps freely at normal
   widths and must keep doing so, so the ellipsis is not applied unconditionally.
   ------------------------------------------------------------------------- */
@container dash-card (max-width: 260px) {
	.dash-card__head { padding: 0.5rem 0.6rem; gap: 0.35rem; }
	.dash-card__heading { min-width: 0; }
	.dash-card__title {
		font-size: 0.95rem; min-width: 0;
		overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
	}
	.dash-card__link { font-size: 0.72rem; white-space: nowrap; }
	.dash-card__actions { gap: 0.35rem; }
}
@container dash-card (max-width: 150px) {
	/* At a single column the title would be two characters and an ellipsis, which
	   says less than the icon already does — and the "View all" link would wrap
	   to shreds. The icon and the settings cog are what remain useful. */
	.dash-card__title { display: none; }
	.dash-card__link { display: none; }
	.dash-card__head { padding: 0.4rem 0.45rem; }
}
.dash-card__cog:hover { color: var(--d-accent); opacity: 1; }
.dash-card__list { display: flex; flex-direction: column; }

/* Recent-edit rows */
.dash-edit {
	display: flex; align-items: center; gap: 0.8rem;
	padding: 0.75rem 1.1rem; border-bottom: 1px solid var(--d-divider);
}
.dash-edit:last-child, .dash-tl:last-child { border-bottom: none; }
.dash-edit__body { flex: 1; min-width: 0; }
.dash-edit__title { font-size: 0.95rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dash-edit__meta { font-size: 0.8rem; color: var(--d-muted); }
.dash-edit__time { font-size: 0.78rem; color: var(--d-muted); white-space: nowrap; }

/* Entity-type chip: a champagne square carrying a /symbols/*.svg glyph. The
   glyph is drawn with a mask + accent fill rather than a background image, so
   it always renders in the brand colour regardless of the source SVG's own
   (inconsistent) fill — the group/default symbols ship near-white. */
.dash-chip {
	width: 30px; height: 30px; flex: none; border-radius: 6px;
	background-color: var(--d-accent-soft);
	display: flex; align-items: center; justify-content: center;
}
.dash-chip::before {
	content: ""; width: 62%; height: 62%;
	background-color: var(--d-accent);
	-webkit-mask: var(--chip-src) center / contain no-repeat;
	        mask: var(--chip-src) center / contain no-repeat;
}
.dash-chip--character { --chip-src: url('/symbols/person.svg'); }
.dash-chip--race      { --chip-src: url('/symbols/elf.svg'); }
.dash-chip--polity    { --chip-src: url('/symbols/castle.svg'); }
.dash-chip--group     { --chip-src: url('/symbols/group.svg'); }
.dash-chip--default,
.dash-chip--item      { --chip-src: url('/symbols/default.svg'); }

/* Featured article — one page, given room */
.dash-featured {
	display: flex; align-items: center; gap: 0.8rem;
	text-decoration: none; color: inherit;
}
.dash-featured__body { min-width: 0; }
.dash-featured__title {
	display: block; font-size: 1.05rem; font-weight: 700; line-height: 1.25;
	overflow: hidden; text-overflow: ellipsis;
}
.dash-featured__type { display: block; font-size: 0.8rem; color: var(--d-muted); }
.dash-featured:hover .dash-featured__title { color: var(--d-accent); }
.dash-featured__lede {
	margin: 0.7rem 0 0; font-size: 0.88rem; line-height: 1.5; color: var(--d-muted);
}
.dash-featured__lede p { margin: 0 0 0.6rem; }
.dash-featured__lede p:last-child { margin-bottom: 0; }

/* ⚠️ Let the excerpt FILL the card, and fade what will not fit.
   The server ships a budget scaled to the card's grid size, but the real limit
   is rendered height — which depends on the viewport, the font and where lines
   happen to wrap, none of which the server can know. So the text is allowed to
   run long and the card does the final truncation. The mask makes the cut read
   as deliberate rather than as a rendering accident.

   Scoped to this widget rather than applied to `.dash-card` generally: every
   other card would inherit the column layout, and that is a change worth making
   deliberately, not as a side effect of fixing one widget's excerpt. */
.dash-widget[data-widget-type="featured-article"] .dash-card {
	display: flex; flex-direction: column;
}
.dash-widget[data-widget-type="featured-article"] .dash-card__pad {
	flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
}
.dash-widget[data-widget-type="featured-article"] .dash-featured__lede {
	flex: 1 1 auto; min-height: 0; overflow: hidden;
}
/* ⚠️ Fade ONLY when the excerpt was actually cut. A short lede in a tall card
   should read as short and finished, not as clipped — the fade is information,
   not decoration. */
.dash-widget[data-widget-type="featured-article"] .dash-featured__lede.is-clipped {
	-webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 1.6em), transparent 100%);
	        mask-image: linear-gradient(to bottom, #000 calc(100% - 1.6em), transparent 100%);
}

/* World stats — 2×2 grid, divider showing through the gaps */
.dash-stats {
	display: grid; grid-template-columns: 1fr 1fr;
	gap: 1px; background: var(--d-divider);
}
.dash-stat { background: var(--d-surface); padding: 1.1rem; display: flex; flex-direction: column; gap: 0.15rem; }
.dash-stat__value { font-size: 1.75rem; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--d-accent); }
.dash-stat__label { font-size: 0.72rem; letter-spacing: 0.05em; text-transform: uppercase; color: var(--d-muted); }

/* Recently added — 2-col entry cards */
.dash-entries { display: grid; grid-template-columns: 1fr 1fr; gap: 0.85rem; padding: 1.1rem; }
.dash-entry {
	border: 1px solid var(--d-divider); border-radius: 6px;
	padding: 0.85rem; display: flex; flex-direction: column; gap: 0.4rem;
}
.dash-entry__name { font-size: 0.95rem; font-weight: 600; line-height: 1.25; }
.dash-entry__blurb { font-size: 0.8rem; color: var(--d-muted); }

/* Tags */
.dash-tag {
	align-self: flex-start; display: inline-flex; align-items: center;
	font-size: 0.7rem; font-weight: 700; letter-spacing: 0.03em;
	padding: 0.15rem 0.55rem; border-radius: 10px; text-transform: uppercase;
}
.dash-tag--accent   { background: var(--d-accent-soft); color: var(--d-accent); }
.dash-tag--accent-2 { background: var(--color-1-lightest); color: var(--color-2); }
.dash-tag--neutral  { background: var(--color-31); color: var(--color-5); }

/* Timeline rows */
.dash-tl { display: flex; align-items: center; gap: 0.8rem; padding: 0.75rem 1.1rem; border-bottom: 1px solid var(--d-divider); }
.dash-tl__dot { width: 8px; height: 8px; flex: none; border-radius: 50%; background: var(--d-accent); }
.dash-tl__body { flex: 1; min-width: 0; }
.dash-tl__name { font-size: 0.95rem; font-weight: 600; }
.dash-tl__era { font-size: 0.8rem; color: var(--d-muted); }
.dash-tl__when { font-size: 0.78rem; font-weight: 700; color: var(--d-accent); white-space: nowrap; }
.dash-tl--link { text-decoration: none; color: inherit; cursor: pointer; transition: background 0.1s ease; }
.dash-tl--link:hover { background: var(--d-accent-soft); }

/* Reference-calendar picker on the timeline widget. */
.dash-tl-picker {
	display: flex; align-items: center; gap: 0.5rem;
	padding: 0.6rem 1.1rem; border-bottom: 1px solid var(--d-divider);
}
.dash-tl-picker__label { font-size: 0.72rem; letter-spacing: 0.03em; text-transform: uppercase; color: var(--d-muted); }
.dash-tl-picker__select {
	flex: 1; min-width: 0; font-size: 0.85rem;
	padding: 0.3rem 0.45rem; border: 1px solid var(--d-divider); border-radius: 5px;
	background: var(--d-surface); color: inherit;
}
.dash-tl-picker__apply {
	font-size: 0.78rem; font-weight: 600; cursor: pointer;
	padding: 0.3rem 0.7rem; border: 1px solid var(--d-divider); border-radius: 5px;
	background: var(--d-surface); color: inherit; transition: border-color 0.1s ease;
}
.dash-tl-picker__apply:hover { border-color: var(--d-accent); }

/* ---------------------------------------------------------------------------
   Weather widget (slice 0045)
   ---------------------------------------------------------------------------
   One row per day, both slots side by side. The day labels are relative
   ("Today", "Tomorrow", "Day 3") because weather is deliberately not tied to a
   calendar -- there is no date to show.
   ------------------------------------------------------------------------- */
/* ⚠️ A CONTAINER, not a media query. Since the minimum size is 1x1 this card
   can be narrower than the viewport by a long way, so what has to stack is
   decided by the CARD's width, which no `@media` can see. That is why the
   breakpoint header's three-token rule does not apply here and why
   `verify-responsive.js` only polices `@media` — the viewport scale and a
   card's own width are different axes. The viewport rule further down stays:
   on a phone the card is full-width but the PAGE is narrow, which is a
   different reason to stack. */
.weather { display: flex; flex-direction: column; min-height: 0; container-type: inline-size; container-name: weather-card; }
.weather__head {
	display: flex; align-items: baseline; gap: 0.5rem;
	padding: 0.55rem 1.1rem; border-bottom: 1px solid var(--d-divider);
}
.weather__region { font-size: 0.85rem; font-weight: 600; }
.weather__season {
	font-size: 0.72rem; letter-spacing: 0.03em; text-transform: uppercase;
	color: var(--d-accent);
}
.weather__days { list-style: none; margin: 0; padding: 0; overflow-y: auto; min-height: 0; }
.weather__day {
	display: flex; align-items: stretch; gap: 0.6rem;
	padding: 0.5rem 1.1rem; border-bottom: 1px solid var(--d-divider);
}
.weather__day:last-child { border-bottom: none; }
/* Today is the one row a GM reads at a glance mid-session. */
.weather__day.is-today { background: var(--d-accent-soft); }
.weather__when {
	flex: none; width: 4.5rem; font-size: 0.78rem; font-weight: 700;
	color: var(--d-muted); padding-top: 0.1rem;
}
.weather__day.is-today .weather__when { color: var(--d-accent); }
.weather__slots { flex: 1; min-width: 0; display: flex; gap: 0.5rem; }
.weather__slot {
	flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 0.05rem;
	padding-left: 0.5rem; border-left: 2px solid var(--d-divider);
}
/* A GM-pinned slot must be distinguishable from a generated one, or there is no
   way to tell what the world decided from what you decided. */
.weather__slot.is-overridden { border-left-color: var(--d-accent-strong); }
.weather__slot-name {
	font-size: 0.66rem; letter-spacing: 0.06em; text-transform: uppercase;
	color: var(--d-muted);
}
.weather__icon { font-size: 1.05rem; line-height: 1.2; }
.weather__condition { font-size: 0.85rem; font-weight: 600; }
.weather__figures { font-size: 0.76rem; color: var(--d-muted); }
.weather__phenomenon { font-size: 0.76rem; font-weight: 600; color: var(--d-accent); }
.weather__note { font-size: 0.74rem; font-style: italic; color: var(--d-muted); }
.weather__controls {
	display: flex; gap: 0.5rem; justify-content: space-between;
	padding: 0.55rem 1.1rem; border-top: 1px solid var(--d-divider);
}
.weather__step {
	font-size: 0.78rem; font-weight: 600; cursor: pointer;
	padding: 0.3rem 0.7rem; border: 1px solid var(--d-divider); border-radius: 5px;
	background: var(--d-surface); color: inherit; transition: border-color 0.1s ease;
}
.weather__step:hover { border-color: var(--d-accent); }

@media (max-width: 60rem) {
	/* On a phone the two slots stack: side by side they were ~70px each, which
	   wrapped "Partly cloudy" to three lines. */
	.weather__slots { flex-direction: column; }
	.weather__day { flex-direction: column; gap: 0.3rem; }
	.weather__when { width: auto; }
}

/* A card dragged down towards 1x1. The fixed 4.5rem day label is wider than a
   one-column card, so it goes first; then the slots stack. Degrades instead of
   overflowing — the worldbuilder decides how small is still readable. */
@container weather-card (max-width: 320px) {
	.weather__day { flex-direction: column; gap: 0.25rem; }
	.weather__slots { flex-direction: column; }
	.weather__when { width: auto; }
}
@container weather-card (max-width: 190px) {
	/* Narrower still: the figures and the AM/PM tag are the first things worth
	   losing, because the icon plus the condition already say the most. */
	.weather__head { padding: 0.4rem 0.6rem; }
	.weather__day { padding: 0.4rem 0.6rem; }
	.weather__figures { font-size: 0.7rem; }
	.weather__controls { padding: 0.4rem 0.6rem; }
	.weather__step { padding: 0.25rem 0.45rem; }
}


@media (max-width: 60rem) {
	/* Collapse to a single column. The saved coordinates are dropped entirely and
	   the cards stack in DOM order — which DashboardLayoutService emits as
	   `ORDER BY y, x`, i.e. exactly the reading order of the designed layout. */
	.dashboard__grid { grid-template-columns: 1fr; grid-auto-rows: auto; }
	.dash-widget { grid-column: 1 / -1; grid-row: auto; }
	.dash-entries { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 60rem) {
	.dashboard { flex-direction: column; }
	.dashboard__sidebar { width: 100%; border-right: none; border-bottom: 1px solid var(--d-divider); }
	.dashboard__spacer { display: none; }
	.dashboard__header { padding: 1.25rem; }
	.dashboard__grid { --dash-pad-x: 1.25rem; padding-bottom: 1.25rem; }
}

/* --- Phase 1: thin title bar (replaces the old .header navbar) --- */
.titlebar {
	width: 100%; height: 2.5rem; flex-shrink: 0;
	background: var(--color-1); color: var(--color-2);
	display: flex; align-items: center; justify-content: flex-end;
	padding: 0 1.25rem;
}
.titlebar__title {
	font-size: 0.95rem; font-weight: 700; letter-spacing: 0.02em;
	color: var(--color-2); text-decoration: none;
}

/* --- App sidebar additions (Change Compendium + collapsible sections) --- */
/* Change Compendium — a native <details> popover at the top of the sidebar. */
.dash-switcher { margin: 0.85rem 0.85rem 0.4rem; position: relative; }
.dash-switcher__current {
	list-style: none; cursor: pointer;
	display: flex; align-items: center; gap: 0.6rem;
	padding: 0.55rem 0.7rem; border: 1px solid var(--d-divider);
	border-radius: 6px; background: var(--color-2-lighter); font-weight: 700;
}
.dash-switcher__current::-webkit-details-marker { display: none; }
.dash-switcher__dot { width: 8px; height: 8px; flex: none; border-radius: 50%; background: var(--d-highlight); }
.dash-switcher__name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dash-switcher__chevron { color: var(--d-muted); transition: transform 0.15s ease; }
.dash-switcher[open] .dash-switcher__chevron { transform: rotate(180deg); }
.dash-switcher__panel {
	position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 20;
	background: var(--d-surface); border: 1px solid var(--d-divider);
	border-radius: 6px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
	padding: 0.5rem; display: flex; flex-direction: column; gap: 2px;
}
.dash-switcher__item {
	display: block; padding: 0.4rem 0.55rem; border-radius: 5px;
	color: var(--d-text); text-decoration: none; font-size: 0.9rem;
	overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dash-switcher__item:hover { background: rgba(0, 0, 0, 0.05); }
.dash-switcher__item.is-current { color: var(--d-accent); font-weight: 700; }
.dash-switcher__footer {
	margin-top: 0.35rem; padding-top: 0.4rem; border-top: 1px solid var(--d-divider);
	display: flex; flex-direction: column; gap: 0.1rem;
}
.dash-switcher__footer a {
	font-size: 0.82rem; padding: 0.3rem 0.55rem; border-radius: 5px;
	color: var(--d-accent); text-decoration: none;
}
.dash-switcher__footer a:hover { background: rgba(0, 0, 0, 0.05); }

/* Collapsible nav section (Articles → types, Folders → tree). */
.dash-nav-group__summary {
	list-style: none; cursor: pointer;
	display: flex; align-items: center; gap: 0.7rem;
	padding: 0.5rem 0.6rem; border-radius: 5px;
	color: var(--d-text); font-weight: 500;
}
.dash-nav-group__summary::-webkit-details-marker { display: none; }
.dash-nav-group__summary:hover { background: rgba(0, 0, 0, 0.05); }
.dash-nav-group__label { flex: 1; font-size: 0.95rem; }
.dash-nav-group__chevron { width: 14px; height: 14px; flex: none; color: var(--d-muted); transition: transform 0.15s ease; }
.dash-nav-group[open] > .dash-nav-group__summary .dash-nav-group__chevron { transform: rotate(180deg); }
.dash-nav-group__children {
	margin: 0.1rem 0 0.4rem 1.55rem; padding-left: 0.55rem;
	border-left: 1px solid var(--d-divider);
	display: flex; flex-direction: column; gap: 1px;
}
.dash-nav-child {
	display: block; padding: 0.3rem 0.5rem; border-radius: 4px;
	font-size: 0.88rem; color: var(--d-text); text-decoration: none;
	overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dash-nav-child:hover { background: rgba(0, 0, 0, 0.05); color: var(--d-accent); }
.dash-nav-group__empty { padding: 0.3rem 0.5rem; font-size: 0.82rem; color: var(--d-muted); font-style: italic; }
/* The reused folder tree sits inside a nav group; trim its default spacing. */
.dash-nav-group__children .folder-tree { margin-top: 1px; font-size: 0.88rem; }

/* Lower "manage" links — quieter than the primary section nav. */
.dashboard__nav--manage { padding-top: 0.15rem; }
.dash-nav-item--minor { font-size: 0.85rem; color: var(--d-muted); font-weight: 500; padding: 0.35rem 0.6rem; }
.dash-nav-item--minor:hover { color: var(--d-accent); background: rgba(0, 0, 0, 0.05); }

/* Account logout button, tucked to the right of the account row. */
.dash-account__logout { margin-left: auto; display: flex; }
.dash-account__logout button {
	background: none; border: none; cursor: pointer; color: var(--d-muted);
	display: flex; align-items: center; padding: 0.3rem; border-radius: 5px;
}
.dash-account__logout button:hover { color: var(--d-accent); background: rgba(0, 0, 0, 0.05); }

/* Overview: inner padding + empty states. */
.dash-card__pad { padding: 1.1rem; }

/* Player-preview banner (slice 0024). Deliberately loud: the worldbuilder is
   looking at the player LAYOUT while still reading content as themselves. */
.dash-preview-banner {
	display: flex; align-items: center; gap: 0.7rem;
	margin: 0 2.25rem 1.25rem;
	padding: 0.7rem 1rem;
	border: 1px solid var(--d-accent); border-radius: 8px;
	background: var(--d-accent-soft); color: var(--d-text);
}
.dash-preview-banner .dash-icon { color: var(--d-accent); flex: none; }
.dash-preview-banner__text { flex: 1; min-width: 0; font-size: 0.88rem; }
.dash-preview-banner__exit { font-size: 0.82rem; font-weight: 600; white-space: nowrap; }
.dashboard__preview-link {
	display: inline-flex; align-items: center; gap: 0.4rem;
	font-size: 0.85rem; font-weight: 600; white-space: nowrap;
	padding: 0.35rem 0.6rem; border-radius: 6px;
	border: 1px solid var(--d-divider); color: var(--d-accent);
	transition: border-color 0.1s ease, background 0.1s ease;
}
.dashboard__preview-link:hover { border-color: var(--d-accent); background: var(--d-accent-soft); }

/* --- Dashboard edit mode (slice 0024 phase 2) -----------------------------
   The toolbar and every rule below are inert until the `dashboard-grid`
   component adds `.grid-stack`/`.is-editing` to the grid, so read mode — and
   every reader — is untouched by any of it. */
.dash-arrange {
	display: flex; align-items: center; gap: 0.6rem;
	padding: 0 2.25rem 0.85rem;
}
.dash-arrange__status { font-size: 0.8rem; color: var(--d-muted); margin-right: auto; }
.dash-arrange:not(.dash-arrange--editing) { justify-content: flex-end; }

/* Gridstack positions items absolutely from its own inline styles, so the CSS
   grid has to stand down for the duration. */
.dashboard__grid.grid-stack { display: block; }

/* ⚠️ Horizontal alignment between the two modes.
   Gridstack positions items ABSOLUTELY and sizes them as a percentage of this
   element (`--gs-column-width: 8.3333%`), and the containing block for an
   absolutely positioned child is the PADDING box. Read mode lays its tracks out
   in the CONTENT box. So the container's own 2.25rem of horizontal padding
   makes every card start further left and run wider the moment edit mode
   begins — the whole arrangement bulges out into the page gutter.

   Trade that padding for margin, keeping exactly HALF A GAP of padding. That
   makes the padding box `content + gap` wide, beginning half a gap left of the
   content origin — the one geometry where Gridstack's per-item margins
   reproduce CSS grid's between-track gaps at every column, edges included:

     Gridstack  x*(W+g)/12 + g/2 - g/2  ==  CSS grid  x*W/12 + x*g/12
     Gridstack  w*(W+g)/12 - g          ==  CSS grid  w*W/12 + w*g/12 - g

   The margin must therefore stay in step with `--dash-pad-x`, which is why that
   is a custom property rather than a literal. */
.dashboard__grid.is-editing {
	padding-left: calc(var(--dash-gap) / 2);
	padding-right: calc(var(--dash-gap) / 2);
	margin-left: calc(var(--dash-pad-x) - var(--dash-gap) / 2);
	margin-right: calc(var(--dash-pad-x) - var(--dash-gap) / 2);
	/* The same correction on the vertical axis. Read mode starts row one at the
	   content-box top; Gridstack insets the first card by half a gap, which
	   would nudge the whole board down on entering edit mode. Padding-top is
	   already 0, so take it off the margin instead. */
	margin-top: calc(var(--dash-gap) / -2);
}
/* Height is NOT set here — see `.dash-card`. Declaring it per-mode is what let
   read mode render every card shorter than the editor showed it. */
.dashboard__grid.is-editing .dash-card {
	outline: 1px dashed var(--d-accent); outline-offset: -1px;
}
/* Cards are dragged by the title bar: the body holds links and a scrollable
   welcome note, and those stay usable while arranging. */
.dashboard__grid.is-editing .dash-card__head { cursor: move; user-select: none; }
.dashboard__grid.is-editing .grid-stack-item-content { background: var(--d-surface); }

/* The resize handle is a bare 20px hit area, rotated -45°. Give it a visible
   corner mark, counter-rotated so it reads as an axis-aligned grip. */
.dashboard__grid.is-editing .grid-stack-item > .ui-resizable-se::after {
	content: ''; position: absolute; inset: 5px;
	transform: rotate(45deg);
	border-right: 2px solid var(--d-accent);
	border-bottom: 2px solid var(--d-accent);
	opacity: 0.6;
}
.grid-stack-placeholder > .placeholder-content {
	border: 2px dashed var(--d-accent); border-radius: 8px;
	background: var(--d-accent-soft);
}

/* --- Add / remove widgets (slice 0024 phase 3) ---------------------------- */
.dash-add { position: relative; }
.dash-add__menu {
	position: absolute; top: calc(100% + 0.35rem); left: 0; z-index: 40;
	min-width: 17rem; padding: 0.3rem;
	background: var(--d-surface);
	border: 1px solid var(--d-border); border-radius: 8px;
	box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14);
}
.dash-add__item {
	display: flex; flex-direction: column; gap: 0.1rem;
	width: 100%; padding: 0.45rem 0.6rem;
	background: none; border: 0; border-radius: 6px;
	text-align: left; cursor: pointer;
}
.dash-add__item:hover:not(:disabled) { background: var(--d-accent-soft); }
.dash-add__item:disabled { cursor: default; opacity: 0.5; }
.dash-add__label { font-size: 0.9rem; font-weight: 600; color: var(--d-text); }
.dash-add__desc { font-size: 0.78rem; color: var(--d-muted); }

/* The remove control is injected by the component on entering edit mode, so it
   exists only while arranging — a reader's card never carries a dead button. */
.dash-card__remove {
	flex: none; margin-left: 0.4rem;
	width: 1.4rem; height: 1.4rem; padding: 0; line-height: 1;
	font-size: 1.1rem; color: var(--d-muted);
	background: none; border: 1px solid transparent; border-radius: 50%;
	cursor: pointer;
}
.dash-card__remove:hover {
	color: #c0392b; /* matches .btn--danger */
	border-color: currentColor;
	background: var(--d-surface);
}
@media (max-width: 60rem) {
	.dash-arrange { padding: 0 1.25rem 0.75rem; }
}
@media (max-width: 60rem) {
	.dash-preview-banner { margin: 0 1.25rem 1rem; flex-wrap: wrap; }
}
.dash-empty { color: var(--d-muted); font-style: italic; font-size: 0.9rem; }
.dash-empty--pad { padding: 1.1rem; }
.dash-empty a { font-style: normal; }

/*---------------------------------------------Nested pages (slice 0021)---------------------------------------------*/

/* Dashboard recent-item rows are links to the page — keep the card look, add
   an affordance on hover rather than default link styling. */
.dash-edit--link, .dash-entry--link { text-decoration: none; color: inherit; cursor: pointer; transition: background 0.1s ease, border-color 0.1s ease; }
.dash-edit--link:hover { background: var(--d-accent-soft); }
.dash-entry--link:hover { border-color: var(--d-accent); }

/* Stub (container) pages in the sidebar page tree — muted + italic. */
.folder-tree__link--stub { color: var(--color-4-lighter); font-style: italic; }

/* Sub-pages listing on a page / container view. */
.article-subpages { margin-top: 2rem; padding-top: 1.25rem; border-top: 1px solid var(--color-31); }
.article-subpages__heading {
	font-size: 0.8rem; letter-spacing: 0.05em; text-transform: uppercase;
	color: var(--color-4-lighter); margin: 0 0 0.6rem;
}
.article-subpages__list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.5rem; }
.article-subpages__link {
	display: inline-block; padding: 0.35rem 0.7rem; border: 1px solid var(--color-31);
	border-radius: 5px; text-decoration: none; color: var(--color-4-darkest); background: var(--color-2);
}
.article-subpages__link:hover { border-color: var(--color-3); }
.article-subpages__link--stub { font-style: italic; color: var(--color-4-lighter); }

/*---------------------------------------------Article-type symbols (slice 0022 item 1)---------------------------------------------*/

/* Default chip source so a chip with no inline --chip-src still shows a glyph. */
.dash-chip { --chip-src: url('/symbols/default.svg'); }

/* A type's symbol as a mask + brand fill — the source SVG's own (inconsistent)
   fill is ignored, only its silhouette shows. Used on the type-picker cards. */
.type-symbol {
	width: 34px; height: 34px; flex: none; border-radius: 6px;
	background-color: var(--color-21);
	display: inline-flex; align-items: center; justify-content: center;
}
.type-symbol::before {
	content: ""; width: 64%; height: 64%;
	background-color: var(--color-1);
	-webkit-mask: var(--sym-src) center / contain no-repeat;
	        mask: var(--sym-src) center / contain no-repeat;
}
/* Type-picker: a centered, responsive grid of compact type cards
   (symbol to the left of name + description). */
.type-picker { max-width: 760px; margin: 0 auto; }
.type-picker__header {
	display: flex; align-items: baseline; justify-content: space-between;
	gap: 1rem; flex-wrap: wrap; margin-bottom: 0.25rem;
}
.type-picker__title { font-size: 1.3rem; font-weight: 700; }
.type-picker__hint { color: var(--color-4); font-size: 0.85rem; margin: 0.25rem 0 0; }
.type-grid {
	display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
	gap: 0.6rem; margin-top: 1.1rem;
}
.type-card {
	display: flex; flex-direction: row; align-items: center; gap: 0.7rem;
	padding: 0.6rem 0.75rem; border: 1px solid var(--color-31); border-radius: 7px;
	background: var(--color-2); text-decoration: none; color: var(--color-4-darkest);
	transition: border-color 0.1s ease, box-shadow 0.1s ease, transform 0.1s ease;
}
.type-card:hover {
	border-color: var(--color-1); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.07);
	transform: translateY(-1px);
}
.type-card .type-symbol { width: 30px; height: 30px; border-radius: 5px; }
.type-card__body { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.type-card__name { font-size: 0.9rem; font-weight: 700; }
.type-card__desc {
	font-size: 0.72rem; color: var(--color-4); line-height: 1.3;
	display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/*---------------------------------------------Persistent topbar (slice 0022 item 3)---------------------------------------------*/

.topbar {
	display: flex; align-items: center; justify-content: space-between; gap: 1rem;
	padding: 0.7rem 1.5rem; min-height: var(--topbar-height); border-bottom: 1px solid var(--d-divider);
	background: var(--d-surface); position: sticky; top: 0; z-index: 20; flex-wrap: wrap;
}
/* The eyebrow keeps the .dashboard__eyebrow identity but overrides its tiny
   uppercase label styling — a readable path of proper-cased names. */
.topbar__eyebrow {
	display: flex; align-items: center; gap: 0.4rem; min-width: 0; flex-wrap: wrap;
	padding: 0; font-size: 0.9rem; letter-spacing: normal; text-transform: none;
}
.topbar__crumb { color: var(--d-muted); text-decoration: none; font-weight: 600; }
.topbar__crumb:hover { color: var(--d-accent); }
.topbar__crumb--current { color: var(--d-text); }
.topbar__sep { color: var(--d-muted); opacity: 0.55; }

.topbar__actions { display: flex; align-items: center; gap: 0.75rem; }
.topbar__search {
	position: relative;
	display: flex; align-items: center; gap: 0.4rem; padding: 0.35rem 0.7rem;
	border: 1px solid var(--d-divider); border-radius: 6px; color: var(--d-muted);
	min-width: 14rem; 
    background: var(--color-2-lighest);
}
.topbar__search:focus-within { border-color: var(--d-accent); color: var(--d-text); }
.topbar__search .dash-icon { width: 15px; height: 15px; }
.topbar__search-btn {
	display: inline-flex; align-items: center; justify-content: center;
	padding: 0; border: 0; background: none; color: inherit; cursor: pointer;
}
/* The collapsed-search affordance exists only below 60rem; without this it would
   render as a second magnifier beside the submit button on every desktop page. */
.topbar__search-open { display: none; }
.topbar__search-input {
	flex: 1 1 auto; min-width: 0; border: 0; background: none; outline: none;
	font-size: 0.85rem; color: var(--d-text);
}
.topbar__search-input::placeholder { color: var(--d-muted); }

/* As-you-type dropdown, anchored under the search box. */
.topbar__search-panel {
	position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 60;
	max-height: 24rem; overflow-y: auto; padding: 0.35rem;
	background: var(--d-surface); border: 1px solid var(--d-divider); border-radius: 8px;
	box-shadow: 0 12px 28px rgba(0, 0, 0, 0.16);
}
.tb-suggest__group {
	font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.06em;
	color: var(--d-muted); padding: 0.4rem 0.55rem 0.2rem;
}
.tb-suggest__item {
	display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem;
	padding: 0.4rem 0.55rem; border-radius: 6px; color: var(--d-text); text-decoration: none;
}
.tb-suggest__item:hover, .tb-suggest__item.is-active { background: var(--d-accent-soft); }
.tb-suggest__title { font-size: 0.88rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tb-suggest__type { font-size: 0.72rem; color: var(--d-muted); white-space: nowrap; flex: none; }
.tb-suggest__empty { padding: 0.6rem 0.55rem; font-size: 0.85rem; color: var(--d-muted); }

.topbar__new { white-space: nowrap; }
.topbar__new .dash-icon { width: 16px; height: 16px; vertical-align: -2px; }

@media (max-width: 40rem) {
	.topbar { padding: 0.6rem 1rem; }
	.topbar__search { min-width: 0; }
}

/* Search results page (slice 0026). Same outer spacing as .admin-card-list so it
   sits consistently with the other shell pages, capped for readable line length. */
.search-page { max-width: 60rem; margin: 1.5rem 0; padding: 0 2rem; }
.search-page__head { margin-bottom: 1.25rem; }
.search-page__title { font-size: 1.5rem; margin: 0 0 0.75rem; color: var(--d-text); }
.search-page__form { display: flex; gap: 0.5rem; }
.search-page__input {
	flex: 1 1 auto; padding: 0.55rem 0.8rem; font-size: 0.95rem; color: var(--d-text);
	border: 1px solid var(--d-divider); border-radius: 6px; background: var(--d-surface);
}
.search-page__input:focus { outline: none; border-color: var(--d-accent); }
.search-page__empty { color: var(--d-muted); font-size: 0.9rem; }
.search-page__count { color: var(--d-muted); font-size: 0.85rem; margin: 0 0 0.75rem; }

.search-results {
	list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px;
	background: var(--d-divider); border: 1px solid var(--d-divider); border-radius: 8px; overflow: hidden;
}
.search-result { background: var(--d-surface); }
.search-result__link { display: flex; gap: 0.8rem; padding: 0.85rem 1rem; color: var(--d-text); text-decoration: none; }
.search-result__link:hover { background: var(--d-accent-soft); }
.search-result__chip {
	width: 30px; height: 30px; flex: none; border-radius: 6px; background-color: var(--d-accent-soft);
	display: flex; align-items: center; justify-content: center;
}
.search-result__chip::before {
	content: ""; width: 62%; height: 62%; background-color: var(--d-accent);
	-webkit-mask: var(--chip-src) center / contain no-repeat;
	        mask: var(--chip-src) center / contain no-repeat;
}
.search-result__chip--event { background-color: var(--d-highlight); }
.search-result__chip--event::before { display: none; }
.search-result__body { min-width: 0; display: flex; flex-direction: column; gap: 0.2rem; }
.search-result__head { display: flex; align-items: baseline; gap: 0.6rem; }
.search-result__title { font-weight: 600; font-size: 0.98rem; }
.search-result__type { font-size: 0.72rem; color: var(--d-muted); text-transform: uppercase; letter-spacing: 0.04em; }
.search-result__snippet { font-size: 0.86rem; color: var(--d-muted); line-height: 1.4; }
.search-result__snippet mark { background: transparent; color: var(--d-accent); font-weight: 600; }

/* Symbol picker on the article-type form. */
.sym-pick { border: none; padding: 0; margin: 0 0 1rem; }
.sym-pick__grid {
	display: grid; grid-template-columns: repeat(auto-fill, minmax(4.5rem, 1fr));
	gap: 0.5rem; margin-top: 0.4rem;
}
.sym-pick__option {
	display: flex; flex-direction: column; align-items: center; gap: 0.3rem;
	padding: 0.55rem 0.25rem; border: 1px solid var(--color-31); border-radius: 6px;
	cursor: pointer; transition: border-color 0.1s ease, background 0.1s ease;
}
.sym-pick__option input { position: absolute; opacity: 0; width: 0; height: 0; }
.sym-pick__icon {
	width: 26px; height: 26px; background-color: var(--color-4-darkest);
	-webkit-mask: var(--sym-src) center / contain no-repeat;
	        mask: var(--sym-src) center / contain no-repeat;
}
.sym-pick__label { font-size: 0.7rem; color: var(--color-4); }
.sym-pick__option:hover { border-color: var(--color-3); }
.sym-pick__option:has(input:checked) {
	border-color: var(--color-1); background: var(--color-21); box-shadow: 0 0 0 1px var(--color-1);
}
.sym-pick__option:has(input:checked) .sym-pick__icon { background-color: var(--color-1); }

/* ==========================================================================
   Admin console (slice 0029) — the application-level `/admin` section.
   Rendered under layouts/main (no compendium shell), so it owns its own page
   padding rather than inheriting the dashboard grid's.
   ========================================================================== */

.admin-console {
	width: 100%;
	margin: 1.5rem 0;
	padding: 0 2rem;
}

.admin-console__head { margin-bottom: 1.5rem; }

.admin-console__title {
	font-size: 1.4rem;
	color: var(--color-1);
	font-weight: bold;
	margin: 0;
}

.admin-console__sub {
	font-size: 0.9rem;
	color: var(--color-4-lighter);
	margin: 0.25rem 0 0;
}

.admin-tabs {
	display: flex;
	gap: 0.25rem;
	margin-top: 1rem;
	border-bottom: 1px solid var(--color-2-darkest);
}

.admin-tab {
	padding: 0.5rem 0.9rem;
	font-size: 0.9rem;
	color: var(--color-4);
	border-bottom: 2px solid transparent;
	margin-bottom: -1px;
}

.admin-tab:hover { color: var(--color-1); }

.admin-tab.is-active {
	color: var(--color-1);
	font-weight: bold;
	border-bottom-color: var(--color-3);
}

/* Stat tiles */
.admin-stats {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
	gap: 0.75rem;
	margin-bottom: 1.5rem;
}

.admin-stat {
	background: var(--color-2);
	border: 1px solid #dadada;
	border-radius: 0.6rem;
	padding: 0.9rem 1.1rem;
	box-shadow: 0 1px 3px rgba(0,0,0,0.06);
}

.admin-stat__value {
	font-size: 1.6rem;
	font-weight: bold;
	color: var(--color-1);
	line-height: 1.1;
}

.admin-stat__label {
	font-size: 0.78rem;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--color-4-lighter);
	margin-top: 0.2rem;
}

.admin-stat__detail {
	font-size: 0.8rem;
	color: var(--color-4-lighter);
	margin-top: 0.4rem;
}

/* Panels */
.admin-panels {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
	gap: 1rem;
	align-items: start;
}

.admin-panel {
	background: var(--color-2);
	border: 1px solid #dadada;
	border-radius: 0.6rem;
	padding: 1.1rem;
	box-shadow: 0 1px 3px rgba(0,0,0,0.06);
}

.admin-panel__title {
	font-size: 1rem;
	font-weight: 700;
	color: var(--color-1);
	margin: 0 0 0.75rem;
}

.admin-panel__empty {
	font-size: 0.9rem;
	color: var(--color-4-lighter);
	margin: 0;
}

/* Tables */
.admin-table {
	width: 100%;
	border-collapse: collapse;
	font-size: 0.9rem;
}

.admin-table th {
	text-align: left;
	font-size: 0.75rem;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--color-4-lighter);
	font-weight: 600;
	padding: 0 0.6rem 0.4rem 0;
	border-bottom: 1px solid var(--color-2-darkest);
}

.admin-table td {
	padding: 0.5rem 0.6rem 0.5rem 0;
	border-bottom: 1px solid var(--color-2-darker);
	vertical-align: middle;
}

.admin-table tr:last-child td { border-bottom: none; }
.admin-table__muted { color: var(--color-4-lighter); }

/* Role pill */
.admin-role {
	display: inline-block;
	font-size: 0.72rem;
	font-weight: bold;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	padding: 0.15rem 0.5rem;
	border-radius: 999px;
	background: var(--color-2-darker);
	color: var(--color-4);
}

.admin-role--admin { background: var(--color-1); color: var(--color-2); }
.admin-role--worldbuilder { background: var(--color-21); color: var(--color-1); }

/* Definition list of instance facts */
.admin-facts {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: 0.4rem 1rem;
	margin: 0;
	font-size: 0.9rem;
}

.admin-facts dt {
	color: var(--color-4-lighter);
	font-size: 0.8rem;
	text-transform: uppercase;
	letter-spacing: 0.04em;
}

.admin-facts dd { margin: 0; color: var(--color-4); }

/* Right-hand button cluster on a list header (Administration + New). */
.admin-card-list__header-actions {
	display: flex;
	gap: 0.5rem;
	align-items: center;
	flex-shrink: 0;
}

/* --- Admin: users (slice 0029 phase 2) ------------------------------------ */

/* Compact button, for the dense action cells in the user table. */
.btn--sm {
	padding: 0.3rem 0.7rem;
	font-size: 0.8rem;
	border-radius: 0.3rem;
}

.admin-toolbar {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 1rem;
	flex-wrap: wrap;
	margin-bottom: 0.75rem;
}

.admin-filter {
	display: flex;
	gap: 0.5rem;
	align-items: center;
	flex-wrap: wrap;
}

.admin-filter input[type="search"],
.admin-filter select {
	padding: 0.4rem 0.6rem;
	font-family: inherit;
	font-size: 0.9rem;
	border: 1px solid var(--color-31);
	border-radius: 0.35rem;
	background: var(--color-2);
	color: var(--color-4);
}

.admin-filter input[type="search"] { min-width: 14rem; }
.admin-filter__clear { font-size: 0.85rem; color: var(--color-4-lighter); }

.admin-console__count {
	font-size: 0.85rem;
	color: var(--color-4-lighter);
	margin: 0 0 0.5rem;
}

.admin-table--users { background: var(--color-2); }
.admin-table--users td { padding: 0.55rem 0.6rem; }
.admin-table__right { text-align: right; }

/* A deactivated account stays legible but visibly stood down. */
.admin-table--users tr.is-inactive .admin-user__name,
.admin-table--users tr.is-inactive td { opacity: 0.65; }

.admin-user__name { font-weight: 600; color: var(--color-1); }

.admin-user__you {
	margin-left: 0.35rem;
	font-size: 0.7rem;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--color-3);
	font-weight: bold;
}

.admin-status {
	font-size: 0.78rem;
	font-weight: 600;
}
.admin-status--active { color: #2E7D5B; }
.admin-status--inactive { color: var(--color-4-lighter); }

.admin-inline {
	display: inline-flex;
	gap: 0.3rem;
	align-items: center;
}

.admin-inline select {
	padding: 0.25rem 0.4rem;
	font-family: inherit;
	font-size: 0.8rem;
	border: 1px solid var(--color-31);
	border-radius: 0.3rem;
	background: var(--color-2);
}

.admin-row-actions {
	display: flex;
	gap: 0.35rem;
	justify-content: flex-end;
	align-items: center;
	flex-wrap: wrap;
}

.admin-pager {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	margin-top: 1rem;
}

/* Admin forms (create user, settings) */
.admin-form {
	background: var(--color-2);
	border: 1px solid #dadada;
	border-radius: 0.6rem;
	padding: 1.25rem;
	box-shadow: 0 1px 3px rgba(0,0,0,0.06);
	max-width: 40rem;
}

.admin-form__field {
	display: block;
	margin-bottom: 0.9rem;
}

.admin-form__field > span {
	display: block;
	font-size: 0.85rem;
	font-weight: 600;
	color: var(--color-4);
	margin-bottom: 0.25rem;
}

.admin-form__field > span em {
	font-weight: normal;
	font-style: normal;
	color: var(--color-4-lighter);
}

.admin-form__field input[type="text"],
.admin-form__field input[type="email"],
.admin-form__field input[type="password"],
.admin-form__field input[type="number"],
.admin-form__field select {
	width: 100%;
	padding: 0.5rem 0.6rem;
	font-family: inherit;
	font-size: 0.9rem;
	border: 1px solid var(--color-31);
	border-radius: 0.35rem;
	background: var(--color-2-lighest);
	color: var(--color-4);
}

.admin-form__help {
	display: block;
	font-size: 0.8rem;
	color: var(--color-4-lighter);
	margin-top: 0.25rem;
}

.admin-form__actions {
	display: flex;
	gap: 0.5rem;
	margin-top: 1.2rem;
}

/* Destructive confirmation */
.admin-panel--danger {
	border-color: var(--color-1-lighter);
	max-width: 42rem;
}

.admin-panel--danger .admin-panel__title { color: var(--color-1); }

.admin-impact {
	margin: 0.75rem 0;
	padding-left: 1.2rem;
	font-size: 0.9rem;
	color: var(--color-4);
}

.admin-impact li { margin-bottom: 0.2rem; }

/* --- Admin: settings (slice 0029 phase 3) --------------------------------- */

.admin-form--wide { max-width: 46rem; }

.admin-fieldset {
	border: none;
	border-top: 1px solid var(--color-2-darkest);
	padding: 0.75rem 0 0;
	margin: 0 0 1rem;
}

.admin-fieldset:first-of-type { border-top: none; padding-top: 0; }

.admin-fieldset__legend {
	font-size: 0.78rem;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--color-4-lighter);
	font-weight: 700;
	padding: 0;
}

.admin-setting { margin-bottom: 1.1rem; }
.admin-setting .admin-form__field { margin-bottom: 0.25rem; }

.admin-setting__check {
	display: flex;
	align-items: flex-start;
	gap: 0.5rem;
}

.admin-setting__check input { margin-top: 0.2rem; }

.admin-setting__check-label {
	font-size: 0.85rem;
	color: var(--color-4-lighter);
}

.admin-setting__meta {
	display: flex;
	gap: 0.6rem;
	align-items: center;
	font-size: 0.75rem;
}

.admin-setting__key {
	font-family: monospace;
	color: var(--color-4-lighter);
	background: var(--color-2-darker);
	padding: 0.05rem 0.3rem;
	border-radius: 0.2rem;
}

.admin-setting__state { color: var(--color-3); font-weight: 600; }
.admin-setting__state--default { color: var(--color-4-lighter); font-weight: normal; }

.admin-reset-list {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-top: 0.6rem;
}


/* --- Admin: maintenance (slice 0029 phase 4) ------------------------------ */

.admin-job {
	display: flex;
	gap: 0.5rem;
	align-items: center;
	flex-wrap: wrap;
	margin-top: 0.75rem;
}

.admin-job select {
	flex: 1;
	min-width: 12rem;
	padding: 0.45rem 0.6rem;
	font-family: inherit;
	font-size: 0.9rem;
	border: 1px solid var(--color-31);
	border-radius: 0.35rem;
	background: var(--color-2);
	color: var(--color-4);
}

.admin-job__note {
	font-size: 0.78rem;
	color: var(--color-4-lighter);
	margin: 0.6rem 0 0;
}

.admin-job__note code,
.admin-form__help code {
	font-family: monospace;
	background: var(--color-2-darker);
	padding: 0.05rem 0.3rem;
	border-radius: 0.2rem;
}

/* --- Admin: table inside a panel ------------------------------------------
   A data table reads as part of the page's card language rather than as a bare
   grid: the panel owns the rounded corners, border and shadow, and the rows run
   edge to edge inside it. The negative inline margins let the row separators
   and hover state reach the panel's edges while the cells keep their padding.
   -------------------------------------------------------------------------- */

.admin-panel--table { padding: 1.1rem 1.25rem 0.6rem; }

.admin-panel__head {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 1rem;
	margin-bottom: 0.6rem;
}

.admin-panel__head .admin-panel__title { margin: 0; }

.admin-panel__count {
	font-size: 0.8rem;
	color: var(--color-4-lighter);
}

.admin-tablewrap {
	margin: 0 -1.25rem;
	padding: 0 0.15rem;
	overflow-x: auto;
}

/* Roomier cells than the compact stat panels, and full-bleed padding so the
   first and last columns line up with the panel's own inset. */
.admin-table--users th,
.admin-table--users td {
	padding: 0.7rem 0.75rem;
}

.admin-table--users th:first-child,
.admin-table--users td:first-child { padding-left: 1.1rem; }

.admin-table--users th:last-child,
.admin-table--users td:last-child { padding-right: 1.1rem; }

.admin-table--users thead th {
	background: var(--color-2-darker);
	border-bottom: none;
	white-space: nowrap;
}

.admin-table--users thead th:first-child { border-radius: 0.4rem 0 0 0.4rem; }
.admin-table--users thead th:last-child { border-radius: 0 0.4rem 0.4rem 0; }

.admin-table--users tbody tr { transition: background-color 0.12s ease; }
.admin-table--users tbody tr:hover { background: var(--color-2-darker); }

.admin-table--users tbody td { border-bottom: 1px solid var(--color-2-darker); }
.admin-table--users tbody tr:last-child td { border-bottom: none; }

/* The row-action controls sit on a hover-tinted row, so give them a solid
   ground of their own instead of letting the tint show through. */
.admin-table--users .admin-inline select,
.admin-table--users .btn--secondary { background-color: var(--color-2); }

/* --- Title bar account + logout ------------------------------------------
   Pages without the app sidebar (admin console, /compendia, the "nothing
   shared with you yet" empty state) have no other chrome, so the way out lives
   here. The wordmark stays right-aligned as before; the account cluster sits to
   its right.
   -------------------------------------------------------------------------- */

.titlebar { gap: 1rem; }

.titlebar__account {
	display: flex;
	align-items: center;
	gap: 0.7rem;
}

.titlebar__user {
	font-size: 0.82rem;
	color: var(--color-2);
	opacity: 0.85;
	white-space: nowrap;
}

.titlebar__logout { display: inline-flex; margin: 0; }

.titlebar__logout-btn {
	font-family: inherit;
	font-size: 0.78rem;
	font-weight: 600;
	color: var(--color-2);
	background: transparent;
	border: 1px solid rgba(245, 242, 242, 0.35);
	border-radius: 0.3rem;
	padding: 0.2rem 0.65rem;
	cursor: pointer;
	transition: background-color 0.15s ease, border-color 0.15s ease;
}

.titlebar__logout-btn:hover {
	background: rgba(245, 242, 242, 0.14);
	border-color: var(--color-2);
}

/* --- Admin: inline-editable cells (slice 0029 follow-up) ------------------
   A cell is a native <details>: the summary is the value + a pen, and opening
   it swaps in the form. `display: none` on the open summary is what makes the
   text appear to *become* an input rather than sit above one.
   -------------------------------------------------------------------------- */

.ie { display: inline-block; min-width: 0; max-width: 100%; }

.ie__view {
	list-style: none;
	cursor: pointer;
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	padding: 0.15rem 0.3rem;
	margin: -0.15rem -0.3rem;
	border-radius: 0.3rem;
	border: 1px solid transparent;
}

.ie__view::-webkit-details-marker { display: none; }

.ie__view:hover {
	background: var(--color-2-lighest);
	border-color: var(--color-31);
}

.ie[open] > .ie__view { display: none; }

.ie__value {
	font-size: 0.9rem;
	color: var(--color-4);
	overflow-wrap: anywhere;
}

/* The pen stays out of the way until the row or cell is hovered — the column
   should read as data first, controls second. */
.ie__pen { opacity: 0; transition: opacity 0.12s ease; flex-shrink: 0; }
.admin-table--users tbody tr:hover .ie__pen,
.ie__view:hover .ie__pen,
.ie__view:focus-visible .ie__pen { opacity: 0.75; }
.ie__view:hover .ie__pen { opacity: 1; }

.ie__icon {
	width: 13px;
	height: 13px;
	display: block;
	fill: none;
	stroke: currentColor;
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.ie__form {
	display: inline-flex;
	align-items: center;
	gap: 0.25rem;
	flex-wrap: wrap;
}

.ie__input {
	font-family: inherit;
	font-size: 0.85rem;
	padding: 0.25rem 0.4rem;
	border: 1px solid var(--color-31);
	border-radius: 0.3rem;
	background: var(--color-2);
	color: var(--color-4);
	max-width: 13rem;
	min-width: 7rem;
}

.ie__input:focus {
	outline: none;
	border-color: var(--color-1);
	box-shadow: 0 0 0 2px rgba(78, 25, 25, 0.12);
}

.ie__btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 1.55rem;
	height: 1.55rem;
	padding: 0;
	border-radius: 0.3rem;
	border: 1px solid var(--color-31);
	background: var(--color-2);
	color: var(--color-4);
	cursor: pointer;
	transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

.ie__btn:hover { border-color: var(--color-1); color: var(--color-1); }
.ie__btn:disabled { opacity: 0.5; cursor: default; }

.ie__btn--save:hover { background: var(--color-1); border-color: var(--color-1); color: var(--color-2); }
.ie__btn--discard:hover { background: var(--color-2-darker); }

/* Error text from a rejected save sits under the input, inside the cell. */
.ie__error {
	display: none;
	flex-basis: 100%;
	font-size: 0.75rem;
	color: var(--color-1);
	font-weight: 600;
}

.ie.is-error .ie__error { display: block; }
.ie.is-error .ie__input { border-color: var(--color-1); }
.ie.is-saving { opacity: 0.7; }

/* The password column is a fixed run of dots for every account, so let it sit
   in a monospace rhythm rather than jiggling with the surrounding text. */
.admin-table--users td .ie[data-field="password"] .ie__value {
	font-family: monospace;
	letter-spacing: 0.05em;
	color: var(--color-4-lighter);
}

/* Error-page action row (403/404/500). */
.error__actions {
	margin-top: 1rem;
	display: flex;
	gap: 0.5rem;
	justify-content: center;
	flex-wrap: wrap;
}

/* --- Article Type authoring (slice 0032) ---------------------------------- */

/* Drag handle inside an inline-edit summary / option row. */
.ie__drag,
.opt-row__handle {
	color: var(--color-31);
	cursor: grab;
	font-size: 0.9rem;
	line-height: 1;
	user-select: none;
	flex-shrink: 0;
}

.ie__drag:active,
.opt-row__handle:active { cursor: grabbing; }

[data-drag-row].is-dragging { opacity: 0.45; }

/* Select-option editor rows */
.opt-rows {
	display: flex;
	flex-direction: column;
	gap: 0.35rem;
}

/* The sub-label inside a Tom Select chip (slice 0037): what this particular
   link IS. Sized to the text it holds so a chip stays chip-shaped. */
.ts-chip { display: inline-flex; align-items: center; gap: 0.35rem; }

/* A light well, deliberately NOT `background: transparent; color: inherit`.
   A chip is dark (--color-1) with light text, so an inherited-colour input read
   as part of the label rather than as something you could type in.

   ⚠️ Scoped under .ts-control on purpose, and it has to be. tom-select.css
   loads AFTER main.css and carries `.ts-control input { color: #303030;
   font-size: 13px; line-height: 18px }` — specificity (0,1,1), which beats a
   bare `.ts-chip__note` (0,1,0) on all three. That rule is why the text came
   out dark grey on a dark chip no matter what this file said. */
.ts-control .ts-chip__note {
	flex: 0 0 auto;
	width: 5rem;
	padding: 0 0.3rem;
	border: 1px solid var(--color-2-darkest);
	border-radius: 0.2rem;
	background: var(--color-2);
	color: var(--color-4-darkest);
	font-family: inherit;
	font-size: 0.8rem;
	line-height: 1.6;
	cursor: text;
	transition: width 0.12s ease;
}
.ts-control .ts-chip__note:hover { background: #fff; }
.ts-control .ts-chip__note:focus {
	outline: 2px solid var(--color-3);
	outline-offset: 1px;
	background: #fff;
	width: 9rem;
}
.ts-control .ts-chip__note::placeholder { color: var(--color-4-lighter); font-style: italic; }
/* Single-value fields: tom-select sets `cursor: pointer` at (0,3,1) on
   `.ts-wrapper.single .ts-control input`, so the caret needs four classes to
   win it back. */
.ts-wrapper.single .ts-control .ts-chip__note { cursor: text; }

.opt-row {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.3rem 0.4rem;
	border: 1px solid var(--color-2-darkest);
	border-radius: 0.35rem;
	background: var(--color-2);
}

.opt-row--new { border-style: dashed; background: transparent; }

.opt-row__value {
	flex: 1;
	min-width: 0;
	padding: 0.35rem 0.5rem;
	font-family: inherit;
	font-size: 0.9rem;
	border: 1px solid var(--color-31);
	border-radius: 0.3rem;
	background: var(--color-2);
	color: var(--color-4);
}

.opt-row__color {
	width: 2.2rem;
	height: 1.9rem;
	padding: 0;
	border: 1px solid var(--color-31);
	border-radius: 0.3rem;
	background: var(--color-2);
	cursor: pointer;
	flex-shrink: 0;
}

/* A select value whose option carries a colour. Emitted ONLY in that case —
   an uncoloured value stays plain text in the list, exactly as before, so this
   feature does not restyle select fields that never opted into it. The colour
   tints the border and washes the background; the text keeps the body's
   contrast rather than fighting an arbitrary hue. */
.opt-chip {
	display: inline-block;
	font-size: 0.82rem;
	padding: 0.1rem 0.5rem;
	border-radius: 999px;
	color: var(--color-4);
	border: 1px solid var(--opt-color);
	background: color-mix(in srgb, var(--opt-color) 18%, var(--color-2));
}

/* Select values laid out inline rather than stacked (slice 0033). Values run
   along the line and wrap; the separator itself is emitted by the template
   (", " between words, a space between chips). The line-height and the chip's
   bottom margin keep wrapped rows from crowding each other. */
.article-infobox__inline {
	line-height: 1.7;
	overflow-wrap: anywhere;
}

.article-infobox__inline .opt-chip { margin-bottom: 0.15rem; }

/* ============================================================================
   OFF-CANVAS NAVIGATION DRAWER (slice 0044, phase 2)
   ----------------------------------------------------------------------------
   Below 60rem the sidebar stops being a column and becomes a drawer over the
   content. Before this it went `width: 100%` and sat ABOVE the page, so every
   navigation meant scrolling past the whole article tree to reach the content.

   Everything here is scoped to `.has-drawer`, which app-shell.js adds on init.
   That is deliberate: without JS there is no drawer and no toggle, and the
   sidebar keeps its old stacked behaviour rather than being translated off
   screen with no way back. The sidebar tree is intentionally no-JS
   (`<details>`, slice 0013) and must stay reachable.
   ========================================================================= */

/* Hidden by default at every width: shown only when JS is present AND narrow. */
.topbar__menu {
	display: none;
	align-items: center; justify-content: center;
	width: var(--tap); height: var(--tap); flex: none;
	margin-right: 0.25rem;
	background: none; border: 1px solid transparent; border-radius: 6px;
	color: var(--d-text); cursor: pointer;
}
.topbar__menu:hover { background: var(--color-2-darker); }
.topbar__menu:focus-visible { outline: 2px solid var(--d-accent); outline-offset: 2px; }

.drawer-backdrop {
	position: fixed; inset: 0; z-index: 50;
	background: rgba(0, 0, 0, 0.45);
	opacity: 0; pointer-events: none;
	transition: opacity 180ms ease;
}

@media (max-width: 60rem) {
	.dashboard.has-drawer .topbar__menu { display: inline-flex; }

	.dashboard.has-drawer .dashboard__sidebar {
		position: fixed; top: 0; bottom: 0; left: 0;
		z-index: 60;
		width: min(86vw, 320px);
		/* Overrides the ≤60rem stacking rule further up the sheet. */
		border-right: 1px solid var(--d-divider); border-bottom: none;
		overflow-y: auto;
		-webkit-overflow-scrolling: touch;
		transform: translateX(-100%);
		transition: transform 200ms ease;
		box-shadow: 0 0 2rem rgba(0, 0, 0, 0.25);
	}
	.dashboard.has-drawer.is-drawer-open .dashboard__sidebar { transform: translateX(0); }
	.dashboard.has-drawer.is-drawer-open .drawer-backdrop { opacity: 1; pointer-events: auto; }
}

/* The drawer is fixed, so the page behind it would otherwise scroll under the
   finger. Set on <body> while open and removed on close. */
body.is-drawer-locked { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
	.dashboard.has-drawer .dashboard__sidebar,
	.drawer-backdrop { transition: none; }
}

/* ============================================================================
   CONTENT SURFACES ON NARROW SCREENS (slice 0044, phase 3)
   ========================================================================= */

@media (max-width: 60rem) {
	/* The topbar must stay ONE ROW. It is `position: sticky` and the editor
	   toolbar sticks directly below it at `top: var(--topbar-height)` -- if the
	   topbar wraps to two rows, that variable becomes a lie and the toolbar is
	   drawn underneath it. `min-height: var(--topbar-height)` then keeps the
	   declared value honest, because nothing inside is taller than it. */
	.topbar {
		flex-wrap: nowrap;
		gap: 0.5rem;
		padding: 0.5rem 0.75rem;
	}
	/* Breadcrumbs keep their TAIL. The page you are on is the useful end of the
	   path, so the head gives way first.
	   `text-overflow` on this element would do nothing -- it styles a box's own
	   inline content, and the crumbs are flex items. The truncation therefore has
	   to happen per crumb, weighted by shrink factor:
	     - head crumbs shrink 100x, so they ellipsise (and vanish) first;
	     - the last crumb shrinks 1x, so it only gives ground once the rest have.
	   `justify-content: flex-end` then pins the trail to the right, so anything
	   still overflowing spills off the LEFT edge and is clipped there. */
	.topbar__eyebrow {
		flex: 1 1 auto; min-width: 0;
		flex-wrap: nowrap; overflow: hidden;
		justify-content: flex-end;
		white-space: nowrap;
	}
	.topbar__crumb {
		flex: 0 100 auto; min-width: 0;
		overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
	}
	/* Matched by position, not by `--current`: a trail whose last entry carries an
	   href renders as a plain `.topbar__crumb`, and it is still the crumb to keep. */
	.topbar__eyebrow > :last-child { flex: 0 1 auto; }
	.topbar__sep { flex: none; }
	/* `flex: 0 1 auto` with `min-width: 0` let this shrink below its own content,
	   so its children overflowed past the right edge -- New Article was not
	   squished, it was pushed off-screen down to a visible sliver. The eyebrow
	   ellipsises, so it can absorb every pixel of shrink on its own. */
	.topbar__actions { flex: none; gap: 0.4rem; }

	/* Search collapses to a magnifier. Sharing the row with the breadcrumb meant
	   both flexed and the field lost, ending up too narrow to read. Expansion is
	   driven by `:focus-within` from a <label for> -- no JS, so the no-JS path to
	   the results page survives. */
	.topbar__search {
		flex: 0 0 auto; min-width: 0; width: var(--tap);
		padding: 0; border-color: transparent; background: none;
	}
	.topbar__search-open {
		position: absolute; inset: 0;
		display: flex; align-items: center; justify-content: center;
		cursor: pointer; color: var(--d-muted);
	}
	.topbar__search-btn { display: none; }
	/* Zero-width rather than `display: none`: the input must stay focusable for
	   the label to target it, and stay in the accessibility tree. */
	.topbar__search-input {
		width: 0; min-width: 0; padding: 0; border: 0; opacity: 0;
	}

	/* Expanded: absolutely positioned against `.topbar` (which is `sticky`, so it
	   is the containing block), clearing the hamburger and covering the crumbs. */
	.topbar__search:focus-within {
		/* Clears the hamburger exactly: topbar padding + the tap square + the row
		   gap. Derived, so it tracks --tap instead of being eyeballed. */
		position: absolute; left: calc(0.75rem + var(--tap) + 0.5rem);
		right: 0.75rem; top: 0.5rem; bottom: 0.5rem;
		width: auto; z-index: 30;
		padding: 0.3rem 0.5rem;
		border-color: var(--d-divider); background: var(--color-2-lighest);
	}
	.topbar__search:focus-within .topbar__search-open { display: none; }
	.topbar__search:focus-within .topbar__search-btn { display: inline-flex; }
	.topbar__search:focus-within .topbar__search-input {
		width: 100%; opacity: 1; padding: 0 0.25rem;
	}
	/* New Article floats instead of competing for topbar width. Still the SAME
	   anchor -- `position: fixed` relocates it, so there is no duplicate link, no
	   second partial, and `canEdit`/`newArticleHref` stay where they were.
	   `.btn` is `display: inline-block`, so flex centring is required or the glyph
	   sits on the text baseline rather than in the middle. */
	.topbar__new-label { display: none; }
	.topbar__new {
		position: fixed; left: 1rem; bottom: 1rem;
		/* Below the drawer backdrop (50), so opening the drawer covers it rather
		   than leaving a button floating over the dimmed page. */
		z-index: 40;
		display: inline-flex; align-items: center; justify-content: center;
		width: var(--fab); height: var(--fab);
		padding: 0; flex: none;
		border-radius: 50%;
		box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);
	}
	/* Baseline nudge is for inline-block text flow; inside a flex box it just
	   pushes the glyph off centre. */
	.topbar__new .dash-icon { width: 22px; height: 22px; vertical-align: 0; }
	/* Reserve the corner so the last line of content is not sitting under it. */
	.dashboard__main { padding-bottom: calc(var(--fab) + 2rem); }


	/* The formatting toolbar is 18 buttons. Wrapped, at 44px touch targets, it
	   becomes three rows of STICKY chrome -- with the topbar that is over a third
	   of a phone screen before any article text. One scrolling row keeps every
	   command reachable at a fixed 44px cost. */
	.tiptap-editor__toolbar {
		flex-wrap: nowrap;
		overflow-x: auto;
		overscroll-behavior-x: contain;
		-webkit-overflow-scrolling: touch;
		scrollbar-width: thin;
	}
	/* Without this the buttons shrink to fit instead of overflowing, and the row
	   scrolls nowhere. */
	.tiptap-editor__toolbar > * { flex: none; }

	/* A 42% label column is fine at 300px of floated infobox; at full width it
	   strands the value in a narrow gutter. */
	.article-infobox__table th { width: 34%; }
}

/* ============================================================================
   ARTICLE DETAILS PANEL (slice 0044, phase 3)
   ----------------------------------------------------------------------------
   `.article-content` is `grid-template-columns: 9fr 3fr` with NO responsive rule,
   so the infobox was rendered into a 3fr column -- about 97px on a phone. The
   ≤60rem rule that looked like it handled this (`float: none; width: 100%`) was
   inherited from an older float layout: `float` is IGNORED on a grid item, and
   the 100% was 100% OF THAT 97px COLUMN. Collapsing the grid is the actual fix.

   Above 60rem nothing here applies and the two-column article is unchanged.
   ========================================================================= */

/* --- First-paint transition suppression (slice 0044) -------------------------
   `preload` is on <html> from the server and removed by js-controller after the
   first paint. Anything whose resting transform is settled late -- by a stylesheet
   that arrived after paint, or by a class JS adds -- would otherwise ANIMATE into
   position on load: the nav drawer visibly slid out on every refresh.

   With scripting off the class is never removed, so there are no CSS transitions
   at all. That is deliberate: the details panel still opens and closes, just
   instantly, and an instant panel beats a page that animates itself on load. */
.preload *,
.preload *::before,
.preload *::after {
    transition: none !important;
    animation-duration: 0s !important;
    animation-delay: 0s !important;
}

.article-details__tab,
.article-details__scrim,
.article-infobox__close { display: none; }

@media (max-width: 60rem) {
	/* One column. Required, not cosmetic: the infobox becomes `position: fixed`
	   below and so leaves the grid entirely -- without this the body would keep
	   the 9fr track and leave the 3fr one standing empty. */
	.article-content { grid-template-columns: 1fr; }

	/* Scoped through the toggle, so the EDIT page's infobox -- which has no
	   toggle and whose inputs must stay in the form -- keeps stacking normally. */
	.article-details__toggle ~ .article-infobox {
		position: fixed; top: 0; right: 0; bottom: 0;
		width: var(--details-w);
		z-index: 46;
		margin: 0; border-radius: 0;
		overflow-y: auto; -webkit-overflow-scrolling: touch;
		transform: translateX(100%);
		transition: transform 220ms ease;
		box-shadow: -0.5rem 0 2rem rgba(0, 0, 0, 0.28);
	}
	.article-details__toggle:checked ~ .article-infobox { transform: translateX(0); }

	/* Close button, upper-left of the panel. The header is STICKY so it survives
	   scrolling -- an infobox can be long, and a close control that scrolls out
	   of reach leaves the scrim and Escape as the only ways out. It already has
	   an opaque background, so nothing shows through underneath. */
	.article-details__toggle ~ .article-infobox .article-infobox__header {
		position: sticky; top: 0; z-index: 1;
		position: -webkit-sticky;
		/* Symmetric so the CENTRED title clears the absolutely-placed button on
		   the left and stays optically centred rather than shoved right. */
		padding-inline: calc(var(--tap) + 0.25rem);
	}
	.article-details__toggle ~ .article-infobox .article-infobox__close {
		position: absolute; left: 0.25rem; top: 50%; transform: translateY(-50%);
		display: inline-flex; align-items: center; justify-content: center;
		width: var(--tap); height: var(--tap);
		border-radius: 0.4rem;
		color: var(--color-2); cursor: pointer;
	}
	.article-infobox__close svg {
		width: 20px; height: 20px;
		fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round;
	}
	.article-details__toggle ~ .article-infobox .article-infobox__close:hover {
		background: rgba(255, 255, 255, 0.14);
	}
	.article-infobox__close:focus-visible {
		outline: 2px solid var(--color-2); outline-offset: -2px;
	}

	/* The tab is the handle ON the folder: it rides the panel's left edge and
	   glides in with it, rather than being a separate control that fades out.
	   It cannot live INSIDE the panel -- the panel is a scroll container, so
	   `overflow-y: auto` computes `overflow-x` to auto too and would clip a child
	   hanging off the left. Instead it stays a sibling and travels the same
	   distance, with the same duration and easing, so the two move as one piece.
	   Being a <label> for the same checkbox, it now closes the panel as well.
	   z-index 46 matches the panel: above the scrim (45), below the drawer (50). */
	.article-details__tab {
		/* 25%, not 50%: halfway between the top of the viewport and centre. Sits
		   clear of where a thumb rests and away from the FAB's diagonal. The
		   translateY(-50%) below centres the tab ON this point, so the number is
		   the tab's midpoint, not its top edge. */
		position: fixed; right: 0; top: 25%;
		transform: translate(0, -50%);
		transition: transform 220ms ease;
		z-index: 46;
		display: flex; flex-direction: column; align-items: center; gap: 0.35rem;
		padding: 0.85rem 0.4rem;
		background: var(--d-surface, var(--color-2));
		color: var(--color-1);
		border: 1px solid var(--color-31); border-right: none;
		border-radius: 0.5rem 0 0 0.5rem;
		box-shadow: -2px 2px 8px rgba(0, 0, 0, 0.18);
		cursor: pointer;
	}
	.article-details__tab-label {
		writing-mode: vertical-rl; text-orientation: mixed;
		font-size: 0.78rem; font-weight: 700; letter-spacing: 0.04em;
	}
	/* Open: the panel occupies the right --details-w of the viewport, so the tab
	   shifts left by exactly that to sit against its edge. */
	.article-details__toggle:checked ~ .article-details__tab {
		transform: translate(calc(-1 * var(--details-w)), -50%);
	}
	/* The input is `.sr-only` and invisible, so the focus ring has to move to the
	   label or keyboard users get no indication at all. */
	.article-details__toggle:focus-visible ~ .article-details__tab {
		outline: 2px solid var(--color-1); outline-offset: 2px;
	}

	.article-details__scrim {
		display: block;
		position: fixed; inset: 0; z-index: 45;
		background: rgba(0, 0, 0, 0.45);
		opacity: 0; pointer-events: none;
		transition: opacity 220ms ease;
	}
	.article-details__toggle:checked ~ .article-details__scrim { opacity: 1; pointer-events: auto; }
}

@media (prefers-reduced-motion: reduce) {
	.article-details__toggle ~ .article-infobox,
	.article-details__tab,
	.article-details__scrim { transition: none; }
}

/* ---------------------------------------------------------------------------
   Admin -> Weather (slice 0045)
   ------------------------------------------------------------------------- */
.admin-weather__picker { margin-bottom: 1rem; }
/* Seasons are edited in place, one <details> each, so a region with six seasons
   is not six pages. Matches the field-settings panel idiom from slice 0036. */
.admin-season {
	border: 1px solid var(--color-4-lightest);
	border-radius: 6px; padding: 0.5rem 0.75rem; margin-bottom: 0.5rem;
}
.admin-season > summary { cursor: pointer; font-weight: 600; }
.admin-season[open] > summary { margin-bottom: 0.75rem; }
.weather-preview td { vertical-align: top; }
.weather-reading__icon { font-size: 1.05rem; }
.weather-reading__phenomenon { font-size: 0.78rem; font-weight: 600; color: var(--color-1-lighter); }

/* Weather override editor (slice 0045 phase 4). */
.weather__slot--link { text-decoration: none; color: inherit; cursor: pointer; border-radius: 0 4px 4px 0; transition: background 0.1s ease; }
.weather__slot--link:hover { background: var(--d-accent-soft); }
.weather-override__generated {
	margin-bottom: 1.25rem; padding: 0.75rem 1rem;
	border: 1px solid var(--color-4-lightest); border-radius: 6px;
}
.weather-override__heading {
	margin: 0 0 0.35rem; font-size: 0.72rem; letter-spacing: 0.06em;
	text-transform: uppercase; color: var(--color-4-lighter);
}
.weather-override__reading { margin: 0 0 0.5rem; font-size: 1rem; }
/* Stacked radios: the three phenomenon states have to be visibly a choice, not
   a row of boxes that could read as independent toggles. */
.weather-override__radio { display: flex; align-items: center; gap: 0.5rem; padding: 0.2rem 0; cursor: pointer; }
