:root {
	--bg: #05050a;
	--bg2: #0d0d16;
	--bg3: #15151f;
	--border: #2a2a40;
	--text: #e8e8f5;
	--text-dim: #8888a8;
	--text-bright: #ffffff;
	--accent: #ff2ec4;
	--accent-dim: #6e1957;
	--green: #39ff8f;
	--red: #ff3b5c;
	--orange: #ff9a3c;
	--yellow: #f5e642;
	--cyan: #28e5ff;
	--purple: #b86bff;
	--font-mono: "Courier New", Courier, monospace;
	/* Legible foreground for text on a solid --accent fill; recomputed per theme
	   by applySettings (settings.js). Fallback for pre-JS / non-game contexts. */
	--accent-ink: #0a0a0a;
	--font-size-base: 16px;
	--density-padding: 16px;
	--density-gap: 8px;
}

/* Theme palettes ([data-theme="…"]) live in the shared single source of truth:
   client/shared/themes.css (linked after this file). The "dark"/Void default
   above IS this client's base theme. Add new themes there, not here. */

/* Theme swatch picker (settings) — replaces the flat theme <select> */
.theme-grid-wrap { max-height: 300px; overflow-y: auto; }
/* Theme-editor scroll areas honour the active theme instead of the OS default */
#te-pane-swatches::-webkit-scrollbar,
.theme-grid-wrap::-webkit-scrollbar,
#theme-editor-rows::-webkit-scrollbar { width: 6px; }
#te-pane-swatches::-webkit-scrollbar-track,
.theme-grid-wrap::-webkit-scrollbar-track,
#theme-editor-rows::-webkit-scrollbar-track { background: var(--bg2); }
#te-pane-swatches::-webkit-scrollbar-thumb,
.theme-grid-wrap::-webkit-scrollbar-thumb,
#theme-editor-rows::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
#te-pane-swatches, .theme-grid-wrap, #theme-editor-rows { scrollbar-width: thin; scrollbar-color: var(--border) var(--bg2); }
.theme-grid-head { font-size: 10px; letter-spacing: 1px; text-transform: uppercase; color: var(--text-dim); margin: 12px 0 6px; }
.theme-grid-head:first-child { margin-top: 0; }
.theme-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.theme-chip { display: flex; flex-direction: column; gap: 5px; background: transparent; border: 1px solid var(--border); border-radius: 6px; padding: 6px; cursor: pointer; text-align: left; }
.theme-chip:hover { border-color: var(--text-dim); }
.theme-chip.selected { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.theme-chip-prev { display: flex; align-items: flex-end; gap: 3px; height: 28px; border: 1px solid; border-radius: 4px; padding: 4px; overflow: hidden; }
.theme-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.theme-chip-name { font-size: 11px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.theme-chip.selected .theme-chip-name { color: var(--accent); }

/* Compact density — used on mobile/desktop "compact" display mode */
[data-density="compact"] {
	--density-padding: 8px;
	--density-gap: 4px;
}

* {
	box-sizing: border-box;
	margin: 0;
	padding: 0;
}

body {
	font-size: var(--font-size-base);
	background: var(--bg);
	color: var(--text);
	font-family: var(--font-mono);
	-webkit-font-smoothing: none;
	-moz-osx-font-smoothing: unset;
	font-smooth: never;
	line-height: 1.5;
	transition:
		background 0.2s ease,
		color 0.2s ease;
	height: 100vh;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	user-select: none;
}

/* Text selection is only meaningful in the scrollback pane and the command
   input — everywhere else (panels, HUD, buttons, minimap, etc.) a click-drag
   is almost always an accidental drag, not a copy attempt. */
#output,
#output *,
#cmd-input,
input,
textarea {
	user-select: text;
}

/* ─── HEADER ─── */
#header {
	background: var(--bg2);
	border-bottom: 1px solid var(--border);
	padding: 6px 16px;
	display: flex;
	align-items: center;
	gap: 16px;
	flex-shrink: 0;
}

#header .logo {
	color: var(--accent);
	font-size: 16px;
	font-weight: bold;
	letter-spacing: 4px;
	text-transform: uppercase;
}

#header .conn-status {
	font-size: 14px;
	line-height: 1;
	flex-shrink: 0;
	cursor: default;
}
#header .conn-status.online {
	color: var(--green);
}
#header .conn-status.offline {
	color: var(--red);
}
#header .conn-status.reconnecting {
	color: var(--orange);
	animation: conn-pulse 1s ease-in-out infinite;
}
@keyframes conn-pulse {
	0%,
	100% {
		opacity: 1;
	}
	50% {
		opacity: 0.25;
	}
}

#header .handle-display {
	margin-left: auto;
	color: var(--accent);
	font-size: 12px;
}

#header .credits-display {
	color: var(--cyan);
	font-size: 12px;
	font-weight: bold;
	padding: 2px 8px;
	border: 1px solid var(--border);
	border-radius: 2px;
	white-space: nowrap;
}

/* ─── MAIN LAYOUT ─── */
#main {
	display: grid;
	grid-template-columns: 240px 1fr;
	grid-template-rows: 1fr;
	flex: 1;
	overflow: hidden;
}

/* Sidebar left (default): sidebar in column 1, output in column 2.
     grid-row: 1 is required on both — without it the auto-placement cursor
     advances past row 1 after placing #output-container (first in DOM) at
     col 2, causing #sidebar to land in row 2 instead of beside it. */
#sidebar {
	grid-column: 1;
	grid-row: 1;
}
#output-container {
	grid-column: 2;
	grid-row: 1;
	border-left: 1px solid var(--border);
}

/* Sidebar right: swap columns */
[data-sidebar="right"] #main {
	grid-template-columns: 1fr 240px;
}
[data-sidebar="right"] #sidebar {
	grid-column: 2;
}
[data-sidebar="right"] #output-container {
	grid-column: 1;
	border-left: none;
	border-right: 1px solid var(--border);
}

[data-density="compact"] #main {
	grid-template-columns: 1fr;
}

[data-density="compact"] #sidebar {
	display: none;
}

/* Narrow viewport — hide sidebar, keep full-height layout in viewport */
@media (max-width: 720px) {
	body {
		height: 100dvh;
	}
	#main {
		grid-template-columns: 1fr;
		grid-template-rows: 1fr;
		overflow: hidden;
	}
	#output-container {
		border-right: none;
	}
	#sidebar {
		display: none;
	}

	/* Header: compress so all items fit on one line */
	#header {
		padding: 4px 8px;
		gap: 6px;
		flex-wrap: nowrap;
		overflow: hidden;
	}
	#header .logo {
		font-size: 11px;
		letter-spacing: 1px;
		flex-shrink: 0;
	}
	#header .conn-status {
		font-size: 10px;
		flex-shrink: 1;
		min-width: 0;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}
	#header .handle-display {
		font-size: 11px;
		flex-shrink: 1;
		min-width: 0;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
		margin-left: auto;
	}
	#header .credits-display {
		font-size: 11px;
		padding: 1px 5px;
		flex-shrink: 0;
	}
	#settings-btn,
	#signout-btn {
		font-size: 13px;
		width: 24px;
		height: 24px;
		flex-shrink: 0;
	}

	#quick-cmds {
		overflow-x: auto;
		flex-wrap: nowrap;
		padding: 6px 10px;
		gap: 6px;
	}
	.qcmd {
		font-size: 13px;
		padding: 8px 14px;
		min-height: 36px;
		flex-shrink: 0;
	}
	#cmd-input {
		font-size: 16px; /* prevents iOS viewport zoom on focus */
	}
	#look-resize-handle {
		display: none;
	}
	#auth-box {
		width: 95vw;
		padding: 18px 14px 24px;
	}
	#auth-ascii {
		font-size: 9px;
	}
	#auth-title {
		font-size: 7px;
		letter-spacing: 0;
	}
}

/* Auth screen — compact/mobile overrides */
[data-density="compact"] #auth-screen {
	align-items: flex-start;
	padding-top: env(safe-area-inset-top, 0px);
}

[data-density="compact"] #auth-box {
	width: 100vw;
	max-width: 100vw;
	min-height: 100dvh;
	border: none;
	border-radius: 0;
	box-shadow: none;
	padding: 24px 20px 40px;
	display: flex;
	flex-direction: column;
	gap: 0;
}

[data-density="compact"] #auth-ascii {
	font-size: 8px;
	margin-bottom: 10px;
}

[data-density="compact"] #auth-title {
	font-size: 6px;
	letter-spacing: 0;
	margin-bottom: 4px;
}

[data-density="compact"] #auth-box .tagline {
	font-size: 12px;
	margin-bottom: 28px;
}

[data-density="compact"] .auth-field {
	margin-bottom: 18px;
}

[data-density="compact"] .auth-field input {
	font-size: 16px; /* prevents iOS zoom on focus */
	padding: 12px 12px;
}

[data-density="compact"] .auth-btn {
	font-size: 15px;
	padding: 14px;
	margin-top: 4px;
}

/* ─── OUTPUT PANE ─── */
#output-container {
	display: flex;
	flex-direction: column;
	overflow: hidden;
	position: relative;
}

/* Transient "aircraft overhead" banner — pinned to the top of the room pane, fades
   in on a flyover and auto-clears. Overlays without stealing pointer events. */
#sky-banner {
	position: absolute;
	top: 0;
	left: 50%;
	transform: translate(-50%, -140%);
	max-width: calc(100% - 16px);
	z-index: 20;
	pointer-events: none;
	padding: 6px 14px;
	border: 1px solid var(--accent);
	border-top: none;
	border-radius: 0 0 8px 8px;
	background: color-mix(in srgb, var(--bg) 88%, var(--accent) 12%);
	box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5), 0 0 12px var(--accent-dim);
	color: var(--text);
	font-size: 13px;
	line-height: 1.4;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	opacity: 0;
	transition: transform 0.35s ease, opacity 0.35s ease;
}
#sky-banner.show {
	transform: translate(-50%, 0);
	opacity: 1;
}
#sky-banner .sky-glyph {
	color: var(--accent);
	margin-right: 4px;
}

#area-pane {
	flex: 0 0 auto;
	max-height: 65%;
	overflow-y: auto;
	padding: 12px var(--density-padding);
	display: flex;
	flex-direction: column;
	background: var(--bg);
}

/* Flight-sim fullscreen: the sim expands to fill the whole output column, pushing the
   text log + command pane down out of view for an immersive cockpit. Toggled from the
   ⛶ button on the sim; the class is cleared when the sim closes. */
body.fsim-fullscreen #area-pane { max-height: none; flex: 1 1 auto; }
body.fsim-fullscreen #area-content { flex: 1 1 auto; display: flex; min-height: 0; }
body.fsim-fullscreen .fsim { flex: 1 1 auto; min-height: 0; }
body.fsim-fullscreen .fsim-view { height: auto; flex: 1 1 auto; min-height: 0; }
body.fsim-fullscreen #output,
body.fsim-fullscreen #look-resize-handle,
body.fsim-fullscreen #bottom-input-wrap { display: none; }

/* Flight-sim hide-panel: a lighter twin of fullscreen — the sim grows to fill the column
   (more outside view; the glass + control rows keep their fixed heights so the cockpit stays
   the same size) but the COMMAND BOX stays put so you can still type. Only the scrollback log
   + its resize handle are folded away. Toggled from the ⊟ button; cleared when the sim closes. */
body.fsim-hidepanel #area-pane { max-height: none; flex: 1 1 auto; }
body.fsim-hidepanel #area-content { flex: 1 1 auto; display: flex; min-height: 0; }
body.fsim-hidepanel .fsim { flex: 1 1 auto; min-height: 0; }
body.fsim-hidepanel .fsim-view { height: auto; flex: 1 1 auto; min-height: 0; }
body.fsim-hidepanel #output,
body.fsim-hidepanel #look-resize-handle { display: none; }

/* Echelon helm — the SAME fullscreen / hide-panel mechanism as the flight sim, so the helm
   console grows to fill the output column (not an OS-fullscreen overlay). Cleared when the helm
   closes. ⛶ folds the command box too; ⊟ keeps it so you can still type. */
body.helm-fullscreen #area-pane,
body.helm-hidepanel #area-pane { max-height: none; flex: 1 1 auto; }
/* Drop the pane's 12px inset in fullscreen so the helm reaches every edge — otherwise the bottom
   padding leaves a gap under the console and steals vertical room from the ship above it. */
body.helm-fullscreen #area-pane { padding: 0; }
body.helm-fullscreen #area-content,
body.helm-hidepanel #area-content { flex: 1 1 auto; display: flex; min-height: 0; }
body.helm-fullscreen .helm-root,
body.helm-hidepanel .helm-root { flex: 1 1 auto; min-height: 0; height: auto; }
body.helm-fullscreen #output,
body.helm-fullscreen #look-resize-handle,
body.helm-fullscreen #bottom-input-wrap { display: none; }
body.helm-hidepanel #output,
body.helm-hidepanel #look-resize-handle { display: none; }

/* Hangar bay fullscreen / hide-panel: the 3D hangar app mounts in #area-pane exactly
   like the flight sim, so it gets the same two immersive layouts — ⛶ fullscreen (pane
   fills the column, log + command box folded away) and ⊟ hide-panel (log gone, command
   box stays). #hb-root already flexes to fill the pane, so only the pane/log rules are
   needed here. Both classes are cleared when the hangar closes. */
body.hb-fullscreen #area-pane,
body.hb-hidepanel #area-pane { max-height: none; flex: 1 1 auto; }
body.hb-fullscreen #area-content,
body.hb-hidepanel #area-content { flex: 1 1 auto; display: flex; min-height: 0; }
body.hb-fullscreen #output,
body.hb-fullscreen #look-resize-handle,
body.hb-fullscreen #bottom-input-wrap,
body.hb-hidepanel #output,
body.hb-hidepanel #look-resize-handle { display: none; }

/* Charter cabin (passenger glass HUD, #ck-hud-root) fullscreen / hide-panel — the same two
   immersive layouts as the flight sim + hangar. The HUD already flexes to fill #area-content
   (height:100%), so only the pane/log rules are needed. ⛶ fullscreen folds the log + command
   box away; ⊟ hide-panel folds only the log. Both classes are cleared when the cabin closes. */
body.ck-fullscreen #area-pane,
body.ck-hidepanel #area-pane { max-height: none; flex: 1 1 auto; }
body.ck-fullscreen #area-content,
body.ck-hidepanel #area-content { flex: 1 1 auto; display: flex; min-height: 0; }
body.ck-fullscreen #output,
body.ck-fullscreen #look-resize-handle,
body.ck-fullscreen #bottom-input-wrap,
body.ck-hidepanel #output,
body.ck-hidepanel #look-resize-handle { display: none; }

/* Flight-sim external chase view: the world fills the whole column so the cockpit controls
   can float over it (see cockpit.js). Grows the pane like hide-panel; the command box stays. */
body.fsim-external #area-pane { max-height: none; flex: 1 1 auto; }
body.fsim-external #area-content { flex: 1 1 auto; display: flex; min-height: 0; }
body.fsim-external .fsim { flex: 1 1 auto; min-height: 0; }
body.fsim-external #output,
body.fsim-external #look-resize-handle { display: none; }

/* Mobile-only hidden state — overrides any inline height styles */
.mob-pane-hidden {
	display: none !important;
}

/* Mobile handle is a tap target — suppress the resize cursor, always show accent colour */
@media (max-width: 720px) {
	#look-resize-handle { cursor: pointer; -webkit-tap-highlight-color: transparent; background: var(--accent); }
}
[data-density="compact"] #look-resize-handle { cursor: pointer; -webkit-tap-highlight-color: transparent; background: var(--accent); }

#look-resize-handle {
	flex: 0 0 6px;
	background: var(--border);
	cursor: ns-resize;
	transition: background 0.15s;
	display: flex;
	align-items: center;
	justify-content: flex-end;
	overflow: visible;
	position: relative;
}
@media (max-width: 720px) {
	#look-resize-handle { flex-basis: 24px; }
}
[data-density="compact"] #look-resize-handle { flex-basis: 24px; }

#mob-room-name-bar {
	display: none;
	flex: 1;
	font-family: var(--font-mono);
	font-size: 10px;
	font-weight: 900;
	color: var(--bg);
	letter-spacing: 1px;
	text-transform: uppercase;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	pointer-events: none;
	padding-left: 6px;
}
/* Show room name on the bar only when the look pane is collapsed */
#area-pane.mob-pane-hidden ~ #look-resize-handle #mob-room-name-bar {
	display: block;
}

#area-pane-toggle {
	display: none;
	flex-shrink: 0;
	color: var(--bg);
	font-family: var(--font-mono);
	font-size: 11px;
	padding: 0 8px;
	pointer-events: none;
}
@media (max-width: 720px) {
	#area-pane-toggle { display: block; }
}
[data-density="compact"] #area-pane-toggle { display: block; }
#look-resize-handle:hover,
#look-resize-handle.dragging {
	background: var(--accent);
}

#look-resize-reset {
	display: none;
	position: absolute;
	right: 8px;
	top: 50%;
	transform: translateY(-50%);
	font-size: 9px;
	color: var(--text-dim);
	background: var(--bg2);
	border: 1px solid var(--border);
	padding: 1px 5px;
	border-radius: 2px;
	cursor: pointer;
	user-select: none;
	line-height: 1.4;
	z-index: 1;
	text-transform: uppercase;
	letter-spacing: 0.5px;
}
#look-resize-handle.manual #look-resize-reset {
	display: block;
}
@media (max-width: 720px) {
	#look-resize-reset { display: none !important; }
}
[data-density="compact"] #look-resize-reset { display: none !important; }
#look-resize-reset:hover {
	color: var(--accent);
	border-color: var(--accent);
}

#area-content {
	flex: 0 0 auto;
	white-space: pre-wrap;
	word-break: break-word;
	line-height: 1.35;
}

/* Collapsible room prose (compact view). Block so it can clamp; the toggle
   sits on its own line beneath. */
.room-desc {
	display: block;
}
/* Woven-object prose (furniture + cameras) — its own paragraph beneath the room
   description, with a blank-line gap so it reads as a second beat. */
.room-furniture {
	display: block;
	margin-top: 0.7em;
}
.room-desc.collapsed {
	display: -webkit-box;
	-webkit-line-clamp: 1;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.room-desc-toggle {
	display: inline-block;
	background: none;
	border: none;
	padding: 0;
	margin: 1px 0;
	color: var(--accent);
	font-family: var(--font-mono);
	font-size: 11px;
	cursor: pointer;
	letter-spacing: 1px;
	-webkit-tap-highlight-color: transparent;
}
.room-desc-toggle:hover {
	text-decoration: underline;
}

/* First-visit tone-setting lore (server injects on the genuine first visit for
   new accounts). A bright specular band sweeps across the letters twice — the
   text is painted by a moving gradient via background-clip:text — then it settles
   to normal prose so it reads as part of the room afterward. One-shot: plays once
   as the line is inserted and comes to rest at the normal text color.

   Safety: at rest (and once the animation ends, fill-mode both holds the 100%
   frame) -webkit-text-fill-color is the solid --text, so the copy is always
   legible even if animations are disabled — the transparent fill that reveals
   the gradient only exists inside the keyframes. */
.intro-lore {
	display: block;
	margin: 5px 0 3px;
	/* accent body with a hot specular glint; swept by animating background-position */
	background: linear-gradient(100deg,
		var(--accent) 0%, var(--accent) 30%,
		#ffe6fb 42%, var(--text-bright) 50%, #ffe6fb 58%,
		var(--accent) 70%, var(--accent) 100%);
	background-size: 300% 100%;
	background-position: 150% 0;
	-webkit-background-clip: text;
	background-clip: text;
	-webkit-text-fill-color: var(--text);
	color: var(--text);
	animation: intro-lore-shimmer 3.6s ease-in-out 1 both;
}
@keyframes intro-lore-shimmer {
	/* reveal the gradient (transparent fill) and drag the glint across twice */
	0%   { -webkit-text-fill-color: transparent; background-position: 150% 0;  text-shadow: 0 0 10px var(--accent-dim); }
	40%  { -webkit-text-fill-color: transparent; background-position: -50% 0;  text-shadow: 0 0 12px var(--accent); }
	42%  { -webkit-text-fill-color: transparent; background-position: 150% 0;  text-shadow: 0 0 12px var(--accent); }
	80%  { -webkit-text-fill-color: transparent; background-position: -50% 0;  text-shadow: 0 0 8px var(--accent-dim); }
	100% { -webkit-text-fill-color: var(--text); background-position: -50% 0;  text-shadow: none; }
}

/* Verb teaching (server `teachVerb()` in messaging.js) — THE STANDARD for the
   first mention of any verb a player hasn't been taught yet. The verb is
   highlighted like the accent link it is and a specular band sweeps across it
   three times, then it comes to rest as an ordinary (still clickable) link.
   Same legibility safety as .intro-lore: the transparent fill only exists inside
   the keyframes, so with animations off the word is a plain accent link. */
.verb-teach {
	font-weight: 700;
	letter-spacing: 0.4px;
	background: linear-gradient(100deg,
		var(--accent) 0%, var(--accent) 34%,
		var(--text-bright) 50%,
		var(--accent) 66%, var(--accent) 100%);
	background-size: 300% 100%;
	background-position: 150% 0;
	-webkit-background-clip: text;
	background-clip: text;
	-webkit-text-fill-color: var(--accent);
	color: var(--accent);
	animation: verb-teach-shimmer 1.8s ease-in-out 3 both;
}
@keyframes verb-teach-shimmer {
	0%   { -webkit-text-fill-color: transparent; background-position: 150% 0; text-shadow: 0 0 8px var(--accent-dim); }
	60%  { -webkit-text-fill-color: transparent; background-position: -50% 0; text-shadow: 0 0 12px var(--accent); }
	100% { -webkit-text-fill-color: var(--accent); background-position: -50% 0; text-shadow: none; }
}

/* Pointing at a room-pane link (server `pointAt()`): rings ripple out of the
   thing the player should click. Cosmetic only — the ::after ring is the whole
   effect and the class is stripped when it finishes. */
.point-ripple {
	position: relative;
}
.point-ripple::after {
	content: "";
	position: absolute;
	inset: -2px -4px;
	border-radius: 6px;
	pointer-events: none;
	border: 1px solid var(--accent);
	animation: point-ripple-out 1.1s ease-out 3;
}
@keyframes point-ripple-out {
	0%   { opacity: 0.9; transform: scale(1);    box-shadow: 0 0 0 0 var(--accent-dim); }
	100% { opacity: 0;   transform: scale(1.55); box-shadow: 0 0 12px 6px transparent; }
}

/* Hero-poster mural reveal (posters plugin): hanging all seven sheets in one
   room spells a word the prose describes as "gleaming" — so it gleams on
   screen — the letters catch light, burn, get swept twice, and settle. Same
   safety as .intro-lore above: the transparent fill that reveals the gradient
   exists only inside the keyframes, so the word stays legible if animations are
   off, and it lands on var(--text-bright) — the per-theme maximum-contrast ink
   (dark on the light themes, white on the dark ones), never a literal colour.
   One-shot: this is an entrance, not a standing effect. */
.mural-word {
	/* fit-content + auto margins: the box is exactly as wide as the lettering
	   (a plain centred block would be as wide as the whole output pane, and the
	   flame canvas measures off this box). */
	display: block;
	width: fit-content;
	max-width: 100%;
	margin: 10px auto 12px;
	text-align: center;
	font-size: 1.6em;
	font-weight: bold;
	letter-spacing: 0.45em;
	text-indent: 0.45em;
	color: var(--text-bright);
	background: linear-gradient(100deg,
		var(--text-bright) 0%, var(--text-bright) 38%,
		#ffffff 46%, var(--cyan) 50%, #ffffff 54%,
		var(--text-bright) 62%, var(--text-bright) 100%);
	background-size: 300% 100%;
	-webkit-background-clip: text;
	background-clip: text;
	animation: mural-ignite 7s ease-in-out 1 both;
}
/* Percentages are of 7s and are matched to burnBehind()'s envelope in flame.js
   (250ms rise / 2.4s hold / 1.4s fade) — the fire holds alone for two full
   seconds before anything legible arrives. */
@keyframes mural-ignite {
	/* the fire has the stage alone — nothing legible is here yet */
	0%   { opacity: 0; -webkit-text-fill-color: transparent; text-shadow: none; background-position: 150% 0; }
	28%  { opacity: 0; -webkit-text-fill-color: transparent; text-shadow: none; background-position: 150% 0; }
	/* the glyphs come up out of the flame as embers, then heat */
	33%  { opacity: 0.5; -webkit-text-fill-color: var(--red); text-shadow: 0 0 20px var(--red), 0 0 40px var(--orange); }
	39%  { opacity: 1; -webkit-text-fill-color: var(--orange); text-shadow: 0 0 30px var(--orange), 0 0 62px var(--red); }
	45%  { opacity: 1; -webkit-text-fill-color: var(--yellow); text-shadow: 0 0 40px var(--yellow), 0 0 78px var(--orange); }
	51%  { opacity: 1; -webkit-text-fill-color: var(--orange); text-shadow: 0 0 24px var(--orange), 0 0 56px var(--red); background-position: 150% 0; }
	/* first sweep */
	56%  { opacity: 1; -webkit-text-fill-color: transparent; text-shadow: 0 0 26px var(--orange); background-position: 150% 0; }
	74%  { opacity: 1; -webkit-text-fill-color: transparent; text-shadow: 0 0 22px var(--cyan); background-position: -50% 0; }
	/* second sweep */
	76%  { opacity: 1; -webkit-text-fill-color: transparent; text-shadow: 0 0 14px var(--accent-dim); background-position: 150% 0; }
	93%  { opacity: 1; -webkit-text-fill-color: transparent; text-shadow: 0 0 16px var(--cyan); background-position: -50% 0; }
	/* settle onto the theme's contrast ink */
	100% { opacity: 1; -webkit-text-fill-color: var(--text-bright); text-shadow: none; background-position: -50% 0; }
}
/* Real fire in the glyphs' own space — the shared particle flame (flame.js),
   the same renderer that burns behind the wanted-HUD stars. The canvas is a
   child of .mural-word, centred on the lettering and painted BEHIND it
   (z-index:-1 inside the word's own stacking context), so the fire occupies
   exactly where the letters will be and then clears to leave them. Absolutely
   positioned, so it never displaces a line of the surrounding prose; it removes
   itself once the last ember dies. */
.mural-word { position: relative; z-index: 1; }
.mural-flame {
	position: absolute; left: 50%; top: 50%; z-index: -1;
	transform: translate(-50%, -50%); pointer-events: none;
}
.mural-reveal { animation: mural-settle 2.8s ease-out 1 both; }
@keyframes mural-settle {
	0%   { opacity: 0.35; filter: brightness(1.5); }
	100% { opacity: 1; filter: brightness(1); }
}
@media (prefers-reduced-motion: reduce) {
	.mural-word { animation: none; }
	.mural-reveal { animation: none; }
}

/* Gate-guard border briefing (gatewarn plugin): a stern, one-time warning block
   set off from ordinary prose by a warning-yellow rule down the side. */
.gate-warning {
	display: block;
	margin: 6px 0 4px;
	padding: 4px 0 4px 10px;
	border-left: 2px solid var(--yellow);
	color: var(--text);
}

#area-pane::-webkit-scrollbar {
	width: 6px;
}
#area-pane::-webkit-scrollbar-track {
	background: var(--bg2);
}
#area-pane::-webkit-scrollbar-thumb {
	background: var(--border);
	border-radius: 3px;
}

#output {
	flex: 1;
	overflow-y: auto;
	padding: 12px var(--density-padding);
	scroll-behavior: smooth;
	display: flex;
	flex-direction: column;
	background: var(--bg);
}

/* Bottom-align messages when sparse without clipping overflow — using
     justify-content:flex-end here breaks upward scrolling once the log fills. */
#output > :first-child {
	margin-top: auto;
}

#output::-webkit-scrollbar {
	width: 6px;
}
#output::-webkit-scrollbar-track {
	background: var(--bg2);
}
#output::-webkit-scrollbar-thumb {
	background: var(--border);
	border-radius: 3px;
}

.msg {
	padding: 2px 0;
	white-space: pre-wrap;
	word-break: break-word;
}
.msg + .msg {
}

.msg-system {
	color: var(--text-dim);
	font-style: italic;
}
.msg-echo {
	color: var(--text-dim);
	opacity: 0.75;
}
/* Macro start/finish banner — bold, tinted inline in the macro's own colour. */
.msg-macro-banner {
	font-weight: bold;
	letter-spacing: 0.5px;
	opacity: 0.9;
}
.msg-look {
	color: var(--text-bright);
}
.msg-move {
	color: var(--cyan);
	opacity: 0.85;
}
.msg-combat {
	color: var(--orange);
}
.msg-combat-incoming {
	color: var(--red);
}
.msg-death {
	color: var(--red);
	font-weight: bold;
}
.msg-ambient {
	color: var(--text-dim);
	font-style: italic;
}
.msg-broadcast {
	color: var(--text-dim);
	border-left: 2px solid var(--border);
	padding-left: 8px;
	margin-left: 2px;
}
.msg-broadcast-ticker {
	color: var(--accent);
	letter-spacing: 0.5px;
	font-style: italic;
	border-left: 2px solid var(--accent);
	padding-left: 8px;
	margin-left: 2px;
}
.msg-broadcast-ascii-art {
	color: var(--text-dim);
	white-space: pre;
	font-size: 12px;
	line-height: 1.3;
	border-left: 2px solid var(--border);
	padding-left: 8px;
	margin-left: 2px;
}
.msg-say {
	color: var(--cyan);
}
.msg-zone-event {
	color: var(--text-dim);
	font-style: italic;
}
.msg-error {
	color: var(--red);
}
.msg-loot {
	color: var(--green);
}
.msg-help {
	color: var(--text);
}
/* Timed tile-task state (job-board work): begin / finished / interrupted. Made to
   stand out from ordinary output with an accent rail + tint so the ~15s window's
   start and end are unmistakable in the log. */
.msg-quest-task {
	color: var(--text);
	font-weight: bold;
	border-left: 3px solid var(--accent);
	background: color-mix(in srgb, var(--accent) 10%, transparent);
	padding: 3px 8px;
	margin: 2px 0;
	border-radius: 0 3px 3px 0;
}
.msg-separator {
	color: var(--border);
	margin: 4px 0;
}

/* Rich text styles used in game output */
.zone-name {
	color: var(--accent);
	font-size: 15px;
	font-weight: bold;
	text-transform: uppercase;
	letter-spacing: 2px;
}
/* District tag under the room name — the neighborhood this room belongs to.
   Colour is set inline per-district; this just sizes it as a quiet subheading. */
.zone-district {
	font-size: 11px;
	font-style: italic;
	letter-spacing: 1px;
	opacity: 0.85;
}
.zone-danger {
	font-size: 11px;
	padding: 1px 5px;
	border-radius: 2px;
	vertical-align: middle;
}
.zone-danger-safe {
	color: var(--green);
	border: 1px solid var(--green);
}
.zone-danger-low {
	color: var(--yellow);
	border: 1px solid var(--yellow);
}
.zone-danger-medium {
	color: var(--orange);
	border: 1px solid var(--orange);
}
.zone-danger-high {
	color: var(--red);
	border: 1px solid var(--red);
}
.zone-danger-lethal {
	color: var(--red);
	border: 1px solid var(--red);
	background: rgba(224, 85, 85, 0.15);
}
.rad-warning {
	color: var(--green);
	font-size: 11px;
}
.pvp-warning {
	color: var(--red);
	font-size: 11px;
}
.safe-warning {
	color: var(--cyan, #59c2d6);
	font-size: 11px;
}
.light-level {
	display: block;
	font-size: 11px;
	font-style: italic;
	margin: 0;
}
.light-clear {
	color: var(--text-dim);
}
.light-blazing {
	color: var(--cyan);
}
.light-bright {
	color: var(--green);
}
.light-dim {
	color: var(--yellow);
}
.light-gloomy {
	color: var(--orange);
}
.light-dark {
	color: var(--red);
}
.light-murk {
	color: var(--purple);
}
.exits-label,
.npcs-label,
.enemies-label,
.corpses-label,
.apartment-label,
.players-label,
.vendors-label,
.buildings-label,
.rooms-label,
.furniture-label,
.items-label {
	color: var(--text-dim);
}
.apartment-label {
	color: var(--cyan);
	font-weight: 600;
}
.vendors-label {
	color: var(--green);
	font-weight: 600;
}
/* Poker table's per-seat chair list — collapsed by default in the area pane. */
.poker-chairs > summary {
	cursor: pointer;
	color: var(--text-dim);
	width: fit-content;
}
.poker-chairs > summary:hover {
	color: var(--accent);
}
.poker-chairs-list {
	display: block;
	padding: 2px 0 0 14px;
}
.enemies-label {
	color: var(--orange);
}
.inv-header,
.stats-header,
.skills-header,
.who-header,
.help-header {
	color: var(--accent);
	font-weight: bold;
	text-transform: uppercase;
	letter-spacing: 2px;
}
.status-flags {
	color: var(--cyan);
	font-size: 12px;
}
.equipped {
	color: var(--green);
}
.skill-category {
	color: var(--accent-dim);
	font-size: 11px;
	text-transform: uppercase;
	letter-spacing: 1px;
}
.help-category {
	color: var(--accent);
}
.ip-gain {
	color: var(--green);
	font-weight: bold;
}
.debug-roll {
	color: var(--text-dim);
	font-family: monospace;
	font-size: 0.9em;
	opacity: 0.8;
}
.death-message {
	color: var(--red);
	font-size: var(--font-size-base);
}
.clone-vat-message {
	color: var(--text-dim);
	font-style: italic;
	font-size: var(--font-size-base);
}
.battle-cry {
	color: var(--orange);
	font-style: italic;
}
/* Prologue tutorial (plugins/prologue): parenthetical control hints + the
   Broadcast-room welcome script rendered as a "screen" of text. */
.hint {
	color: var(--text-dim);
	font-style: italic;
	font-size: 0.9em;
}
.broadcast-line {
	color: var(--green);
	font-family: var(--font-mono, monospace);
	text-shadow: 0 0 6px rgba(80, 255, 140, 0.35);
}
/* Inline combat markup (see server/engine/combat.js) */
.hit-part {
	color: var(--cyan);
}
.dmg-dealt,
.dmg-taken {
	color: var(--text-bright);
	font-weight: bold;
}
.dmg-type {
	color: var(--purple);
	font-size: 11px;
	text-transform: uppercase;
	letter-spacing: 1px;
}
.crit-tag {
	color: var(--yellow);
	font-weight: bold;
}
.crit-tag-in {
	color: var(--text-bright);
	font-weight: bold;
	letter-spacing: 1px;
}
/* Power attack. Deliberately shaped like .crit-tag — a crit and a pow never
   render two badges (the server collapses them to one CRITICAL POWER tag). */
.pow-tag {
	color: var(--orange);
	font-weight: bold;
	letter-spacing: 1px;
}
/* The dodge move decorates the incoming attack line rather than adding one. */
.dodge-tag {
	color: var(--cyan);
	font-weight: bold;
}
.flee-fail {
	color: var(--yellow);
}
/* Stance: the tag on the change line, and the dim modifier summary after it. */
.stance-tag {
	font-weight: bold;
	letter-spacing: 1px;
}
.stance-mods {
	color: var(--text-dim);
	font-size: 11px;
}
/* Persistent HUD chip. Stance is state, not an event — it belongs here rather
   than being re-announced in the output pane every attack cycle. */
.stance-chip {
	display: inline-block;
	padding: 2px 8px;
	border: 1px solid currentColor;
	border-radius: 3px;
	font-family: var(--font-mono, monospace);
	font-size: 10px;
	font-weight: bold;
	letter-spacing: 1px;
	opacity: 0.9;
}
.stance-berserk {
	color: var(--red);
}
.stance-aggressive {
	color: var(--orange);
}
.stance-normal {
	color: var(--text-dim);
}
.stance-cautious {
	color: var(--cyan);
}
.stance-pacifist {
	color: var(--green);
}
.hpbar {
	font-family: var(--font-mono);
	letter-spacing: -1px;
}
.hpbar.hp-high {
	color: var(--green);
}
.hpbar.hp-mid {
	color: var(--yellow);
}
.hpbar.hp-low {
	color: var(--red);
}
.hp-count {
	color: var(--text-dim);
	font-size: 12px;
}
.hpbar-label {
	color: var(--accent);
	font-weight: bold;
}
.mutation-tag {
	color: var(--purple);
	font-style: italic;
}
.overdose-warning,
.turret-warning {
	color: var(--red);
	font-weight: 600;
}
.addiction-warning,
.withdrawal-warning,
.outcast-warning {
	color: var(--orange);
	font-style: italic;
}
.item-grant {
	color: var(--cyan);
}

/* ─── ENVIRONMENT HUD ─── */
#env-hud-sidebar {
	display: flex;
	flex-direction: column;
	gap: 2px;
	font-size: 13px;
	color: var(--text);
	cursor: pointer;
	padding: 2px;
	border-radius: 2px;
	transition: background 0.15s;
}
#env-hud-sidebar .env-hud-row {
	display: flex;
	align-items: center;
	gap: 8px;
}
#env-hud-sidebar .env-hud-weather {
	font-size: 11px;
}
#env-hud-sidebar:hover {
	background: var(--bg3);
}
#env-hud-sidebar .env-icon {
	font-size: 15px;
}
#env-hud-sidebar .env-clock {
	color: var(--text-bright);
	font-weight: bold;
	letter-spacing: 0.5px;
}
#env-hud-sidebar .env-temp {
	color: var(--cyan);
	margin-left: auto;
}
#env-hud-sidebar .env-weather-label {
	color: var(--text);
	font-size: 11px;
}
#env-hud-sidebar .env-precip-label {
	color: var(--blue);
	font-size: 11px;
	margin-left: auto;
}

/* Forecast panel — mirrors the dialogue panel's modal pattern */
#forecast-panel {
	display: none;
	position: fixed;
	inset: 0;
	background: transparent;
	z-index: 100;
	align-items: center;
	justify-content: center;
}
#forecast-panel.active {
	display: flex;
}
#forecast-box {
	background: var(--bg2);
	border: 1px solid var(--accent);
	padding: 24px;
	max-width: 420px;
	width: 90%;
	border-radius: 2px;
}
#forecast-header {
	color: var(--accent);
	font-weight: bold;
	margin-bottom: 14px;
	text-transform: uppercase;
	letter-spacing: 1px;
}
#forecast-today {
	display: flex;
	flex-direction: column;
	gap: 4px;
	margin-bottom: 14px;
	padding-bottom: 12px;
	border-bottom: 1px solid var(--border);
}
#forecast-today .ft-row {
	display: flex;
	gap: 10px;
	font-size: 13px;
}
#forecast-today .ft-label {
	color: var(--text-dim);
	width: 56px;
	flex-shrink: 0;
}
#forecast-today .ft-val {
	color: var(--text);
	text-transform: capitalize;
}
#forecast-today .ft-temp {
	color: var(--cyan);
	font-weight: bold;
}
#forecast-today .ft-precip {
	color: var(--blue);
}
#forecast-section-label {
	color: var(--text-dim);
	font-size: 10px;
	text-transform: uppercase;
	letter-spacing: 1px;
	margin-bottom: 8px;
}
#forecast-days {
	display: flex;
	flex-direction: column;
	gap: 6px;
}
.forecast-day-row {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 7px 10px;
	background: var(--bg3);
	border: 1px solid var(--border);
	border-radius: 2px;
	font-size: 13px;
}
.forecast-day-row.fd-today {
	border-color: var(--accent);
}
.forecast-day-row .fd-label {
	color: var(--text-dim);
	width: 56px;
	flex-shrink: 0;
}
.forecast-day-row .fd-icon {
	font-size: 16px;
	width: 22px;
	text-align: center;
}
.forecast-day-row .fd-weather {
	color: var(--text-dim);
	flex: 1;
	text-transform: capitalize;
}
.forecast-day-row.fd-severe .fd-weather {
	color: var(--warning, #e8a13a);
}
.forecast-day-row .fd-warn {
	color: var(--warning, #e8a13a);
	cursor: help;
}
/* Sky-wide named weather-event announcements (ion storm, acid rain). */
.weather-event {
	color: var(--warning, #e8a13a);
	font-weight: bold;
	letter-spacing: 0.02em;
}
.forecast-day-row .fd-humid {
	color: var(--text-dim);
	font-size: 12px;
	width: 56px;
	text-align: right;
	flex-shrink: 0;
}
.forecast-day-row .fd-wind {
	color: var(--text-dim);
	font-size: 12px;
	width: 52px;
	text-align: right;
	flex-shrink: 0;
}
.forecast-day-row .fd-temp {
	color: var(--cyan);
	font-weight: bold;
}

/* Visibility dimming — filter: brightness() on #area-pane and #output.
     Both have explicit background: var(--bg) so the background is inside the
     filter and dims too (critical for light themes). vis >= 0.6 clears it. */
#area-pane,
#output {
	transition: filter 1.75s ease;
}

/* Navigation rows (Exits, Buildings, Rooms) opt out of the low-light dimming so
   you can always find your way out of a dark room. --vis-undim (set in
   environment.js) is the inverse of the pane's brightness filter, so this
   cancels it back to full brightness. */
.exits-row,
.buildings-row,
.rooms-row {
	filter: brightness(var(--vis-undim, 1));
}

/* Keyboard-collapse transition for mobile */
@media (max-width: 720px) {
	#area-pane,
	#output {
		transition: filter 1.75s ease, max-height 0.2s ease;
	}
}

/* Weather FX overlay — decorative particle/haze layer pinned over the top pane.
   Position/size are set in JS (synced to #area-pane's on-screen box each frame).
   Never intercepts input; sits above the scene text but below modals/HUD. */
#weather-fx-canvas {
	position: fixed;
	pointer-events: none;
	z-index: 5;
}

/* ─── INPUT BAR ─── */
#input-area {
	border-top: 1px solid var(--border);
	padding: 10px 16px;
	display: flex;
	align-items: center;
	gap: 8px;
	background: var(--bg2);
	flex-shrink: 0;
}

#prompt {
	color: var(--accent);
	align-self: center;
	user-select: none;
}

#cmd-input {
	flex: 1;
	background: transparent;
	border: none;
	color: var(--text-bright);
	font-family: var(--font-mono);
	font-size: 14px;
	outline: none;
	caret-color: var(--accent);
}

#cmd-input::placeholder {
	color: var(--text-dim);
}

/* Quick command buttons */
#quick-cmds {
	border-top: 1px solid var(--border);
	padding: 6px 16px;
	display: flex;
	gap: 6px;
	flex-wrap: wrap;
	background: var(--bg);
	flex-shrink: 0;
}

.qcmd {
	background: var(--bg3);
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 11px;
	padding: 2px 8px;
	cursor: pointer;
	border-radius: 2px;
	transition: all 0.1s;
}
.qcmd:hover {
	border-color: var(--accent);
	color: var(--accent);
}

/* ─── SIDEBAR ─── */
#sidebar {
	display: flex;
	flex-direction: column;
	overflow: hidden;
	background: var(--bg2);
	position: relative;
	align-self: stretch;
	height: 100%;
	user-select: none;
}

.sidebar-section {
	padding: 12px;
	border: 1px solid var(--border);
	margin-bottom: -1px;
	position: relative;
	display: flex;
	flex-direction: column;
}

/* Scrollable content area — a resized (fixed-height) panel scrolls this
   instead of clipping. Scrollbar stays hidden until the panel is hovered. */
.sidebar-section-body {
	flex: 1 1 auto;
	min-height: 0;
	overflow-y: auto;
	overflow-x: hidden;
	scrollbar-width: thin;
	scrollbar-color: transparent transparent;
}
.sidebar-section-body:hover {
	scrollbar-color: var(--border) transparent;
}
.sidebar-section-body::-webkit-scrollbar {
	width: 8px;
}
.sidebar-section-body::-webkit-scrollbar-track {
	background: transparent;
}
.sidebar-section-body::-webkit-scrollbar-thumb {
	background: transparent;
	border-radius: 4px;
}
.sidebar-section-body:hover::-webkit-scrollbar-thumb {
	background: var(--border);
}

.sidebar-label {
	font-size: 10px;
	text-transform: uppercase;
	letter-spacing: 2px;
	color: var(--text-dim);
	margin-bottom: 8px;
}

/* Online roster */
.players-online-count {
	color: var(--accent);
	letter-spacing: 0;
}
.players-online-empty {
	font-size: 11px;
	color: var(--text-dim);
	padding: 2px 0;
}
.players-online-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 6px;
	padding: 4px 0;
	border-bottom: 1px solid var(--border);
}
.players-online-row:last-child {
	border-bottom: none;
}
.players-online-who {
	display: flex;
	flex-direction: column;
	min-width: 0;
}
.players-online-handle {
	font-size: 12px;
	font-weight: 600;
	color: var(--text-bright);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.players-online-handle.is-self {
	color: var(--accent);
}
.players-online-zone {
	font-size: 10px;
	color: var(--text-dim);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.players-online-whisper {
	flex: none;
	background: transparent;
	border: 1px solid var(--accent);
	color: var(--accent);
	font-family: var(--font-mono);
	font-size: 10px;
	padding: 2px 6px;
	cursor: pointer;
	border-radius: 2px;
}
.players-online-whisper:hover {
	background: var(--accent);
	color: var(--accent-ink, #000);
}

/* Header bar — holds the lock button; movable like a section when unlocked */
.sidebar-header {
	flex: none;
	display: flex;
	align-items: center;
	gap: 6px;
	padding: 4px 6px;
	background: var(--bg2);
	border-bottom: 1px solid var(--border);
	z-index: 5;
}

.sidebar-lock-btn {
	width: 28px;
	height: 28px;
	box-sizing: border-box;
	background: var(--bg3);
	border: 1px solid var(--border);
	border-radius: 3px;
	/* Single-colour SVG padlock, drawn in the theme accent via currentColor.
	   Lock/unlock state is shown by the open/closed glyph + the border. */
	color: var(--accent);
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0;
	cursor: pointer;
	transition: border-color 0.1s, background 0.1s;
}
.sidebar-lock-btn svg {
	display: block;
}
.sidebar-lock-btn:hover {
	border-color: var(--accent);
}
.sidebar-lock-btn.unlocked {
	border-color: var(--accent2);
	background: var(--bg2);
}
[data-sidebar="right"] .sidebar-header {
	justify-content: flex-end;
}

/* Move / resize / reset-sizes button group — only shown while unlocked */
.sidebar-modes {
	display: flex;
	gap: 4px;
}
.sidebar-mode-btn {
	width: 26px;
	height: 28px;
	box-sizing: border-box;
	background: var(--bg3);
	border: 1px solid var(--border);
	border-radius: 3px;
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 13px;
	padding: 0;
	cursor: pointer;
	transition: border-color 0.1s, color 0.1s;
}
.sidebar-mode-btn:hover {
	border-color: var(--accent);
	color: var(--accent);
}
.sidebar-mode-btn.active {
	border-color: var(--accent2);
	color: var(--accent2);
	background: var(--bg2);
}

/* Restore-hidden-panels dropdown — only shown while the sidebar is unlocked */
.sidebar-restore {
	position: relative;
}
.sidebar-restore-btn {
	height: 28px;
	box-sizing: border-box;
	padding: 0 8px;
	background: var(--bg3);
	border: 1px solid var(--border);
	border-radius: 3px;
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 11px;
	cursor: pointer;
	transition: border-color 0.1s, color 0.1s;
}
.sidebar-restore-btn:hover {
	border-color: var(--accent);
	color: var(--accent);
}
.sidebar-restore-menu {
	position: absolute;
	top: calc(100% + 4px);
	left: 0;
	min-width: 130px;
	background: var(--bg2);
	border: 1px solid var(--border);
	border-radius: 3px;
	padding: 4px;
	z-index: 20;
	box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
[data-sidebar="right"] .sidebar-restore-menu {
	left: auto;
	right: 0;
}
.sidebar-restore-item {
	display: block;
	width: 100%;
	text-align: left;
	padding: 5px 8px;
	background: none;
	border: none;
	border-radius: 2px;
	color: var(--text);
	font-family: var(--font-mono);
	font-size: 12px;
	cursor: pointer;
}
.sidebar-restore-item:hover {
	background: var(--bg3);
	color: var(--accent);
}
.sidebar-restore-empty {
	padding: 5px 8px;
	color: var(--text-dim);
	font-size: 11px;
	font-style: italic;
}


/* Collapse toggle — only actionable while the sidebar is unlocked */
.sidebar-label-collapsible {
	display: flex;
	align-items: center;
	justify-content: space-between;
}
.sidebar-collapse-btn {
	display: none;
	background: none;
	border: none;
	color: var(--text-dim);
	font-size: 11px;
	line-height: 1;
	padding: 0 2px;
	cursor: pointer;
}
.sidebar-collapse-btn:hover {
	color: var(--accent);
}
#sidebar.drag-mode .sidebar-collapse-btn {
	display: inline-block;
}
.sidebar-section.collapsed > *:not(.sidebar-label) {
	display: none !important;
}

/* Drag mode: sections and the header bar get grab cursor */
#sidebar.drag-mode .sidebar-section,
#sidebar.drag-mode .sidebar-header {
	cursor: grab;
}
#sidebar.drag-mode .sidebar-section:active,
#sidebar.drag-mode .sidebar-header:active {
	cursor: grabbing;
}
/* Buttons inside the draggable header keep a pointer cursor (grab the bar, not them) */
#sidebar.drag-mode .sidebar-header button {
	cursor: pointer;
}

/* Dragged section / header goes semi-transparent */
.sidebar-section.dragging,
.sidebar-header.dragging {
	opacity: 0.4;
}

/* Resize mode: a grabbable strip on each panel's bottom edge */
.sidebar-resize-handle {
	display: none;
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 7px;
	cursor: ns-resize;
	z-index: 6;
}
#sidebar.resize-mode .sidebar-resize-handle {
	display: block;
}
#sidebar.resize-mode .sidebar-resize-handle:hover,
.sidebar-section.resizing .sidebar-resize-handle {
	background: var(--accent);
	box-shadow: 0 0 8px var(--accent);
}
/* Highlight panel edges so it's clear what's resizable */
#sidebar.resize-mode .sidebar-section {
	border-bottom-color: var(--accent2);
}

/* Thin line showing where the dragged section will land */
#sidebar-drop-indicator {
	display: none;
	position: absolute;
	left: 0;
	right: 0;
	height: 2px;
	background: var(--text-dim);
	pointer-events: none;
	z-index: 100;
	transition: background 0.08s, box-shadow 0.08s;
}
#sidebar-drop-indicator.snapping {
	background: var(--accent);
	box-shadow: 0 0 10px var(--accent), 0 0 4px var(--accent);
}

/* Trash-can drop zone — tints the look/output panes while a panel is dragged
   off the bar toward them, signalling that releasing will remove it. */
#sidebar-trash-indicator {
	display: none;
	position: fixed;
	align-items: center;
	justify-content: center;
	background: rgba(0, 0, 0, 0.5);
	border: 2px dashed var(--red);
	border-radius: 6px;
	pointer-events: none;
	z-index: 1000;
	opacity: 0;
	transition: opacity 0.1s;
}
#sidebar-trash-indicator.active {
	display: flex;
	opacity: 1;
}
#sidebar-trash-indicator .trash-card {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 8px;
	color: var(--red);
	transform: scale(0.9);
	transition: transform 0.1s;
}
#sidebar-trash-indicator.active .trash-card {
	transform: scale(1);
}
#sidebar-trash-indicator .trash-icon {
	font-size: 48px;
	line-height: 1;
}
#sidebar-trash-indicator .trash-label {
	font-size: 11px;
	text-transform: uppercase;
	letter-spacing: 2px;
}

/* DOM anchor for drop insertion — zero height always so lock/unlock never shifts layout */
.sidebar-drop-end {
	display: block;
	flex: none;
	height: 0;
	min-height: 0;
	overflow: hidden;
}

/* Reset sidebar order button in settings */
.sidebar-reset-btn {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 11px;
	padding: 4px 10px;
	cursor: pointer;
	border-radius: 2px;
}
.sidebar-reset-btn:hover {
	border-color: var(--accent);
	color: var(--accent);
}

/* Vitals bars */
.vital {
	margin-bottom: 6px;
}
.vital-label {
	font-size: 11px;
	color: var(--text-dim);
	display: flex;
	justify-content: space-between;
	margin-bottom: 2px;
}
.vital-bar-bg {
	background: var(--bg3);
	height: 6px;
	border-radius: 1px;
}
.vital-bar {
	height: 100%;
	border-radius: 1px;
	transition: width 0.3s;
}
.vital-hp .vital-bar {
	background: var(--green);
}
.vital-sanity .vital-bar {
	background: var(--purple);
}
.vital-hunger .vital-bar {
	background: var(--yellow);
}
.vital-thirst .vital-bar {
	background: var(--cyan);
}
.vital-radiation .vital-bar {
	background: var(--orange);
}
.vital-stamina .vital-bar {
	background: var(--accent);
}
.vital-horny .vital-bar {
	background: #c06;
}
.vital-bar.low {
	background: var(--red);
}

/* Zone info */
#zone-name-display {
	font-size: 11px;
	color: var(--accent);
	text-transform: uppercase;
	letter-spacing: 1px;
	margin-bottom: 4px;
}

/* Location d-pad — replaces the old exit buttons. Auto mode fills the section
   width up to the max cell size (--dpad-max); the resize button overrides with
   fixed full (100%) / half (50%) sizes. */
.loc-dpad {
	position: relative;
	--dpad-max: 42px;
	margin-top: 6px;
}
.loc-dpad .dpad-panel-inner {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 2px;
}
.loc-dpad .dpad-row {
	display: flex;
	gap: 2px;
	justify-content: center;
}
.loc-dpad .dpad-btn,
.loc-dpad .dpad-center {
	font-size: 15px;
}
.loc-dpad .dpad-ud {
	font-size: 12px;
}

/* auto — grow to fill the width, capped at --dpad-max, kept square */
.loc-dpad[data-dpad-mode="auto"] .dpad-panel-inner,
.loc-dpad[data-dpad-mode="auto"] .dpad-row {
	width: 100%;
}
.loc-dpad[data-dpad-mode="auto"] .dpad-btn,
.loc-dpad[data-dpad-mode="auto"] .dpad-center {
	flex: 1 1 0;
	width: auto;
	height: auto;
	max-width: var(--dpad-max);
	aspect-ratio: 1;
}

/* full — fixed at the max size */
.loc-dpad[data-dpad-mode="full"] .dpad-btn,
.loc-dpad[data-dpad-mode="full"] .dpad-center {
	flex: none;
	width: var(--dpad-max);
	height: var(--dpad-max);
}

/* half — fixed at 50% of the max size */
.loc-dpad[data-dpad-mode="half"] .dpad-btn,
.loc-dpad[data-dpad-mode="half"] .dpad-center {
	flex: none;
	width: calc(var(--dpad-max) / 2);
	height: calc(var(--dpad-max) / 2);
	font-size: 11px;
}
.loc-dpad[data-dpad-mode="half"] .dpad-ud {
	font-size: 9px;
}

.dpad-resize-btn {
	position: absolute;
	top: 0;
	right: 0;
	width: 18px;
	height: 18px;
	padding: 0;
	line-height: 1;
	font-size: 11px;
	background: var(--bg3);
	border: 1px solid var(--border);
	color: var(--text-dim);
	border-radius: 2px;
	cursor: pointer;
	z-index: 1;
	transition: border-color 0.1s, color 0.1s;
}
.dpad-resize-btn:hover {
	border-color: var(--accent);
	color: var(--accent);
}

.chat-btn {
	background: var(--bg3);
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 11px;
	padding: 2px 8px;
	cursor: pointer;
	margin: 2px;
	border-radius: 2px;
	transition: all 0.1s;
}
.chat-btn:hover {
	border-color: var(--accent);
	color: var(--accent);
}

/* Settings button */
#settings-btn {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-size: 14px;
	width: 28px;
	height: 28px;
	cursor: pointer;
	border-radius: 2px;
	transition: all 0.15s;
	line-height: 1;
}
#settings-btn:hover {
	border-color: var(--accent);
	color: var(--accent);
	transform: rotate(45deg);
}

#signout-btn {
	background: transparent;
	border: 1px solid var(--red);
	color: var(--red);
	font-size: 14px;
	width: 28px;
	height: 28px;
	cursor: pointer;
	border-radius: 2px;
	transition: all 0.15s;
	line-height: 1;
	margin-left: 6px;
}
#signout-btn:hover {
	opacity: 0.7;
}

/* Clickable nouns in room text — exits, items, NPCs, enemies, corpses.
     Underlined and colored by category so it's clear what kind of thing
     it is and that it's interactive, without having to type the command. */
.action-link {
	text-decoration: underline;
	text-decoration-style: dotted;
	text-underline-offset: 3px;
	cursor: pointer;
	transition:
		color 0.1s,
		text-decoration-style 0.1s;
	font-weight: 600;
}
.action-link:hover {
	text-decoration-style: solid;
	filter: brightness(1.25);
}

.exit-link {
	color: var(--cyan);
}
.dir-tag {
	color: var(--text-dim);
	font-weight: bold;
	margin-right: 4px;
}
.building-link {
	color: var(--purple);
}
.room-nav-link {
	color: var(--cyan);
}
.furniture-link {
	color: var(--orange);
}
/* Furniture woven into the room prose (flags.woven): reads as body text, not a
   bright link. Stays clickable — a faint dotted underline surfaces on hover. */
.action-link.furniture-woven {
	color: inherit;
	font-weight: inherit;
	text-decoration: none;
}
.action-link.furniture-woven:hover {
	text-decoration: underline;
	text-decoration-style: dotted;
	text-underline-offset: 3px;
	filter: none;
}
/* Slot reel window: the whole ASCII box is the pull lever. Kill the inline
   underline (it's a block, not a word) and give it a tactile hover instead. */
.action-link.slot-pull {
	display: inline-block;
	text-decoration: none;
	color: inherit;
}
.action-link.slot-pull pre {
	margin: 0;
	transition: filter 0.1s;
}
.action-link.slot-pull:hover {
	filter: brightness(1.2);
}
.player-link {
	color: var(--purple);
}
.npc-link {
	color: var(--green);
}
.enemy-link {
	color: var(--red);
}
.corpse-link {
	color: var(--text-dim);
}
/* Service/verb command links (flight hangar & ramp: hangar/refuel/buy/embark/…).
   Carry data-action="cmd" data-cmd — a bright yellow so the actionable verbs
   stand out from the surrounding description prose. */
.cmd-link {
	color: var(--yellow);
}
/* ─── MINIMAP ─── */
#minimap-section {
	font-family: var(--font-mono);
	background: var(--bg);
	border: 1px solid var(--border);
	border-radius: 2px;
	padding: 10px;
}
/* Room+gap grid: rooms sit on the even tracks, exit connectors on the odd
   (gap) tracks between them. A gap with a connector = a walkable exit; an empty
   gap = a wall. Rooms carry a danger-tinted fill so danger reads at a glance
   (mirrors the full-map .map-room.danger-* palette for consistency). The 9×9
   track layout is shared by the sidebar, HUD, and mobile minimaps; each sets its
   own --mm-room/--mm-gap size below. */
#minimap-grid,
#minimap-grid-hud,
#minimap-grid-mob {
	display: grid;
	position: relative; /* containing block for the abspos GPS-line SVG below */
	/* Edge-to-edge 1:1: a 9×9 tile window (R=4 in minimap.js), tiles touch — roads and
	   buildings render their own icon footprint, no connector/gap tracks. */
	grid-template-columns: repeat(9, var(--mm-room));
	grid-template-rows: repeat(9, var(--mm-room));
	justify-content: center;
	align-content: center;
	line-height: 1;
	/* Promote to its own compositor layer so the move-slide (slideMinimap's
	   translate animation) moves the grid as one rasterized bitmap. Without this
	   the browser re-paints each cell per frame at sub-pixel offsets, opening
	   hairline seams between adjacent tiles that flicker in and out during motion. */
	backface-visibility: hidden;
	will-change: transform;
}
#minimap-grid {
	--mm-room: 1.7em;
	font-size: 10px;
}
.mm-c {
	display: flex;
	align-items: center;
	justify-content: center;
	white-space: pre;
	text-align: center;
	box-sizing: border-box;
	min-width: 0;
	overflow: hidden;
}
.mm-link {
	color: var(--text-dim);
	opacity: 0.75;
}
/* Named zone-icon SVG on a minimap tile (symFor → flags.icon). Drawn as a mask
   filled with currentColor, so the icon takes the tile's text colour. */
.mm-icon {
	width: 82%;
	height: 82%;
	background: currentColor;
	-webkit-mask: var(--zi) center / contain no-repeat;
	mask: var(--zi) center / contain no-repeat;
	pointer-events: none;
}
/* Enterable building tile — a door you step through, not a room you stand in.
   Clickable (go <building>); glyph ▣ from symFor. */
.mm-room.mm-building {
	cursor: pointer;
	color: var(--accent, #59c2d6);
	border: 1px solid currentColor;
	border-radius: 2px;
	opacity: 0.9;
}
/* Nearby-but-unreachable tiles (beyond the reachable BFS) render faint so it's
   clear there are tiles there you just can't reach; their connectors dim too. */
.mm-room.mm-unreachable {
	opacity: 0.3;
	filter: grayscale(0.55);
}
.mm-link.mm-dim {
	opacity: 0.25;
}
/* District boundary connector — the street between two neighborhoods, faded and
   dashed so district edges read as edges rather than a seamless grid. */
.mm-link.mm-boundary {
	opacity: 0.35;
}
/* The divider runs perpendicular to the street it crosses: an east-west connector
   gets a vertical bar between the two tiles; a north-south one a horizontal bar. */
.mm-link.mm-boundary-v {
	border-left: 1px dashed var(--text-dim);
}
.mm-link.mm-boundary-h {
	border-bottom: 1px dashed var(--text-dim);
}
/* Gateway tile — a door out of your district into an adjacent one. The sidebar
   scopes to your own neighborhood, so instead of the real foreign room you see a
   dashed, hollow marker carrying that district's initials (coloured inline), a
   signpost that reads "another place lies this way". */
.mm-gateway {
	border: 1px dashed currentColor;
	border-radius: 2px;
	font-size: 8px;
	font-style: italic;
	opacity: 0.7;
}
/* The Curtain (perimeter wall): a hard-light shimmer ring along the city's land edge;
   the glacis kill-zone reads with a faint hazard tint; the one gate pulses as an opening. */
.mm-curtain {
	box-shadow: inset 0 0 0 1px rgba(122, 196, 255, 0.42), inset 0 0 3px rgba(122, 196, 255, 0.28);
}
.mm-glacis {
	box-shadow: inset 0 0 0 1px rgba(224, 120, 90, 0.45);
}
.mm-gate {
	color: #d6f4ff;
	font-weight: bold;
	z-index: 2;
	animation: mm-gate-pulse 2.4s ease-in-out infinite;
}
@keyframes mm-gate-pulse {
	0%, 100% { box-shadow: inset 0 0 0 1px #7fe0ff, 0 0 4px rgba(127, 224, 255, 0.55); }
	50% { box-shadow: inset 0 0 0 1px #aef0ff, 0 0 8px rgba(127, 224, 255, 0.95); }
}
/* Avenue View / Auto-walk toggles sit in the minimap section's top-right corner
   (kept out of the .sidebar-label so it doesn't leak into the section's reorder
   title), side by side in a small flex row. */
#minimap-section { position: relative; }
.mm-toggle-row {
	position: absolute;
	top: 8px;
	right: 8px;
	display: flex;
	gap: 4px;
	z-index: 3;
}
.mm-avenue-toggle {
	font-family: var(--font-mono);
	font-size: 8px;
	letter-spacing: 1px;
	text-transform: uppercase;
	line-height: 1.4;
	background: var(--bg);
	border: 1px solid var(--border);
	color: var(--text-dim);
	border-radius: 2px;
	padding: 1px 5px;
	cursor: pointer;
	transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.mm-avenue-toggle:hover { border-color: var(--accent); color: var(--text); }
.mm-avenue-toggle:disabled { opacity: 0.35; cursor: default; }
.mm-avenue-toggle:disabled:hover { border-color: var(--border); color: var(--text-dim); }
/* Zoom +/- cluster in the minimap's bottom-right corner (mirrors the top-right
   toggle row). Overlays the map, semi-opaque so the tiles beneath still read. */
.mm-zoom-row {
	position: absolute;
	bottom: 8px;
	right: 8px;
	display: flex;
	gap: 4px;
	z-index: 3;
}
.mm-zoom-row .mm-avenue-toggle { background: color-mix(in srgb, var(--bg) 82%, transparent); }
.mm-avenue-toggle.active {
	border-color: var(--accent);
	color: var(--accent);
	background: color-mix(in srgb, var(--accent) 14%, transparent);
}
/* Icon variant of the minimap toggles (Run / Avenue / Auto-walk): square, with the
   glyph sized up from the 8px text label and the uppercase/letter-spacing (meant
   for words) neutralised so the emoji sits centered. */
.mm-icon-btn {
	font-size: 12px;
	line-height: 1;
	letter-spacing: 0;
	padding: 3px 4px;
	min-width: 24px;
	text-align: center;
}
/* Run mode gets its own warm "exertion" tint so it reads distinctly from the
   accent-cyan Avenue/Auto active states. */
#mm-run-toggle.active {
	border-color: #ffb454;
	color: #ffb454;
	background: color-mix(in srgb, #ffb454 16%, transparent);
}
/* Avenue View on the minimap draws the SVG road (shared .av-* classes with the
   full map) ON TOP of the tile's normal danger/custom fill, so tile colours still
   read at a glance. Overflow is allowed so road arms can meet the gap bands. */
.mm-room.mm-avenue-cell {
	overflow: visible;
}
.mm-msg {
	grid-column: 1 / -1;
	color: var(--text-dim);
	text-align: center;
}
/* Crossing mode — the void trail view (renderCrossing). The grid keeps its track
   template, so the single crossing block spans every track to fill the minimap. */
.mm-crossing-mode { background: radial-gradient(ellipse at center, var(--bg2), var(--bg)); }
.mm-crossing {
	grid-column: 1 / -1; grid-row: 1 / -1;
	position: relative; overflow: hidden;
	display: flex; flex-direction: column; align-items: center; justify-content: center;
	gap: 0.15em; width: 100%; height: 100%; padding: 0.3em 0;
	font-family: var(--font-mono); color: var(--text-dim);
}
/* Drifting ash/grit — faint specks sliding across the waste behind the trail. */
.mm-crossing::before {
	content: ''; position: absolute; inset: -20%; z-index: 0; pointer-events: none; opacity: 0.5;
	background-image:
		radial-gradient(1px 1px at 20% 30%, color-mix(in srgb, var(--text-dim) 55%, transparent) 50%, transparent),
		radial-gradient(1px 1px at 70% 60%, color-mix(in srgb, var(--text-dim) 45%, transparent) 50%, transparent),
		radial-gradient(1px 1px at 45% 85%, color-mix(in srgb, var(--text-dim) 40%, transparent) 50%, transparent),
		radial-gradient(1px 1px at 85% 15%, color-mix(in srgb, var(--text-dim) 50%, transparent) 50%, transparent);
	background-size: 120px 120px, 90px 90px, 140px 140px, 110px 110px;
	animation: mm-x-ash 9s linear infinite;
}
@keyframes mm-x-ash { from { background-position: 0 0,0 0,0 0,0 0; } to { background-position: -40px 120px, 30px 90px, -25px 140px, 20px 110px; } }
[data-motion="off"] .mm-crossing::before { animation: none; }
.mm-crossing > * { position: relative; z-index: 1; }
.mm-x-cap { font-size: 0.62em; letter-spacing: 0.22em; color: var(--accent); opacity: 0.9;
	text-shadow: 0 0 8px color-mix(in srgb, var(--accent) 55%, transparent); padding-bottom: 0.35em;
	border-bottom: 1px solid color-mix(in srgb, var(--accent) 22%, transparent); margin-bottom: 0.25em; }
.mm-x-trail { display: flex; flex-direction: column; align-items: center; gap: 0; line-height: 1; }
/* Vertical trail connector: a dashed line that flows downward — the going, drawn
   on toward the far gate. Motion sells the trudge; static falls back cleanly. */
.mm-x-row { position: relative; display: flex; align-items: center; justify-content: center; padding: 0.08em 0; }
.mm-x-row + .mm-x-row::before {
	content: ''; position: absolute; top: -0.42em; left: 50%; width: 2px; height: 0.62em; transform: translateX(-50%);
	background: repeating-linear-gradient(to bottom,
		color-mix(in srgb, var(--text-dim) 70%, transparent) 0 2px, transparent 2px 4px);
	background-size: 100% 4px; animation: mm-x-flow 1.4s linear infinite; opacity: 0.75;
}
@keyframes mm-x-flow { to { background-position: 0 4px; } }
[data-motion="off"] .mm-x-row + .mm-x-row::before { animation: none; }
.mm-x-node { font-size: 0.9em; }
.mm-x-walked { color: var(--text-dim); opacity: 0.65; } /* trail behind — dim, walked */
.mm-x-you .mm-x-here {
	color: var(--accent); font-size: 1.15em;
	text-shadow: 0 0 8px color-mix(in srgb, var(--accent) 70%, transparent); animation: mm-x-pulse 1.8s ease-in-out infinite;
}
@keyframes mm-x-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.55; } }
[data-motion="off"] .mm-x-you .mm-x-here { animation: none; }
.mm-x-fog { color: var(--text-dim); opacity: 0.55; }  /* the blind gamble ahead */
.mm-x-gate .mm-x-node { color: var(--green); text-shadow: 0 0 7px color-mix(in srgb, var(--green) 50%, transparent); } /* the far gate — nearly across */
.mm-x-dead .mm-x-node { color: var(--red); }    /* dead-end detour */
/* Branch tick beside "you" — a fork/divert (⋔) or a risk-for-loot detour (?). */
.mm-x-branch { font-size: 0.7em; margin-left: 0.3em; cursor: default; }
.mm-x-divert { color: var(--cyan); }
.mm-x-gamble { color: var(--yellow); }
/* Node captions: absolutely placed beside the centred trail so the dots stay aligned.
   Default sits to the right; "you" sits to the left (branch ticks take its right side). */
.mm-x-lbl { position: absolute; top: 50%; left: calc(50% + 0.85em); transform: translateY(-50%);
	white-space: nowrap; font-size: 0.5em; letter-spacing: 0.03em; color: var(--text-dim); opacity: 0.85; pointer-events: none; }
.mm-x-you .mm-x-lbl { left: auto; right: calc(50% + 0.85em); color: var(--accent); opacity: 0.95; }
.mm-x-gate .mm-x-lbl { color: var(--green); }
.mm-x-dead .mm-x-lbl { color: var(--red); }
.mm-x-cue { opacity: 0.7; }
.mm-x-cue .mm-x-node { font-size: 0.7em; color: var(--text-dim); }
.mm-x-legend { font-size: 0.5em; letter-spacing: 0.04em; color: var(--text-dim); opacity: 0.8; margin-top: 0.15em; }
/* Hard ground — a seeded rough stretch (void_hard). Walked-through reads red-dim; standing on one throbs. */
.mm-x-walked.mm-x-hot .mm-x-node { color: var(--red); opacity: 0.8; text-shadow: 0 0 5px color-mix(in srgb, var(--red) 45%, transparent); }
.mm-x-you.mm-x-hot .mm-x-here { color: var(--red); text-shadow: 0 0 11px color-mix(in srgb, var(--red) 75%, transparent); animation: mm-x-hotpulse 1s ease-in-out infinite; }
.mm-x-you.mm-x-hot .mm-x-lbl { color: var(--red); opacity: 1; }
@keyframes mm-x-hotpulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
[data-motion="off"] .mm-x-you.mm-x-hot .mm-x-here { animation: none; }
.mm-x-hazard { font-size: 0.72em; margin-left: 0.3em; color: var(--red); text-shadow: 0 0 5px color-mix(in srgb, var(--red) 50%, transparent); cursor: default; }
.mm-room {
	position: relative;
	font-weight: bold;
	border-radius: 2px;
	border: 1px solid transparent;
	color: var(--text);
}
.mm-room.danger-safe {
	background: rgba(35, 100, 55, 0.4);
	border-color: rgba(80, 180, 100, 0.45);
}
.mm-room.danger-low {
	background: rgba(115, 100, 20, 0.4);
	border-color: rgba(210, 190, 60, 0.45);
}
.mm-room.danger-medium {
	background: rgba(125, 75, 20, 0.45);
	border-color: rgba(220, 140, 50, 0.5);
}
.mm-room.danger-high,
.mm-room.danger-lethal {
	background: rgba(115, 30, 30, 0.45);
	border-color: rgba(200, 60, 60, 0.5);
}
.mm-room.danger-lethal {
	box-shadow: 0 0 5px rgba(200, 60, 60, 0.5);
}
.mm-room.mm-styled {
	background: none;
	border-color: transparent;
}
.mm-current {
	color: var(--accent, #5b9af5);
	font-weight: bold;
	background: none;
	border-color: transparent;
}
/* Player "you are here" beacon — a glowing accent core plus a pulsing locator
   ring. Replaces the old () glyph, which was hard to read at small sizes. Shared
   by the sidebar/HUD/mobile minimaps, the full map, and the legend swatches. */
.mm-current, .map-current { position: relative; overflow: visible; z-index: 2; }
.mm-current::before, .map-current::before {
	content: '';
	position: absolute;
	top: 50%;
	left: 50%;
	width: 0.72em;
	height: 0.72em;
	transform: translate(-50%, -50%);
	border-radius: 50%;
	background: var(--accent);
	box-shadow: 0 0 0 1.5px var(--bg), 0 0 7px 1px var(--accent);
	z-index: 1;
}
.mm-current::after, .map-current::after {
	content: '';
	position: absolute;
	top: 50%;
	left: 50%;
	width: 0.72em;
	height: 0.72em;
	transform: translate(-50%, -50%);
	border-radius: 50%;
	border: 1.5px solid var(--accent);
	animation: you-ping 1.7s ease-out infinite;
}
@keyframes you-ping {
	0%   { opacity: 0.7; transform: translate(-50%, -50%) scale(0.75); }
	100% { opacity: 0;   transform: translate(-50%, -50%) scale(2.2); }
}
[data-motion="off"] .mm-current::after,
[data-motion="off"] .map-current::after {
	animation: none;
	opacity: 0.35;
	transform: translate(-50%, -50%) scale(1.5);
}

/* Map popup */
#map-panel {
	display: none;
	position: fixed;
	inset: 0;
	background: transparent;
	/* Above the Tablet OS chassis (z-index 9200) so the map pops over it when
	   launched from the tablet's Map tile — same convention as the music player. */
	z-index: 9250;
	align-items: center;
	justify-content: center;
	/* Floating window, not a modal: the full-screen container lets clicks fall
	   through to whatever's behind (the tablet, the game) — only #map-box catches
	   pointer events. So there's no backdrop to click-to-close; use the Close button. */
	pointer-events: none;
}
#map-panel.active {
	display: flex;
}
#map-box {
	pointer-events: auto;
	/* Tablet-OS chassis look (mirrors tablet-os.js --tos-surface system) so the
	   popup reads as the same handheld device — but the map grid inside stays
	   full colour (only a recessed-bezel CRT sheen sits over it, no accent wash). */
	--map-surface-hi: color-mix(in srgb, var(--accent) 16%, var(--bg2));
	--map-surface-lo: color-mix(in srgb, var(--accent) 5%, var(--bg2));
	--map-bevel-hi: color-mix(in srgb, white 45%, transparent);
	--map-bevel-lo: color-mix(in srgb, black 45%, transparent);
	background: linear-gradient(160deg, var(--map-surface-hi), var(--bg2) 55%, var(--map-surface-lo));
	border: 2px solid color-mix(in srgb, var(--bg2) 35%, #000 65%);
	padding: 16px;
	border-radius: 18px;
	box-shadow:
		inset 0 1px 0 var(--map-bevel-hi),
		inset 0 0 0 1px rgba(0, 0, 0, 0.5),
		0 18px 50px rgba(0, 0, 0, 0.72),
		0 0 40px color-mix(in srgb, var(--accent) 16%, transparent);
	max-width: 94vw;
	max-height: 90vh;
	overflow: auto;
	scrollbar-width: thin;
	scrollbar-color: var(--accent) var(--bg);
}
#map-box::-webkit-scrollbar {
	width: 10px;
	height: 10px;
}
#map-box::-webkit-scrollbar-track {
	background: var(--bg);
	border-radius: 2px;
}
#map-box::-webkit-scrollbar-thumb {
	background: var(--accent);
	border-radius: 2px;
	border: 2px solid var(--bg);
}
#map-box::-webkit-scrollbar-thumb:hover {
	background: var(--accent-dim);
}
#map-box::-webkit-scrollbar-corner {
	background: var(--bg);
}
#map-box h2 {
	color: var(--accent);
	font-size: 14px;
	letter-spacing: 3px;
	text-transform: uppercase;
	margin-bottom: 16px;
	text-shadow: 0 0 8px color-mix(in srgb, var(--accent) 55%, transparent);
}
#map-header {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 16px;
	margin-bottom: 16px;
	/* Grab handle: drag empty header space to move the whole popup (the tabs,
	   slider and buttons inside keep their own pointer/click behaviour). */
	cursor: move;
	user-select: none;
	touch-action: none;
}
#map-box.map-dragging {
	cursor: move;
}
#map-header h2 {
	margin-bottom: 0;
}
#map-tabs {
	flex: 0 0 auto;
	display: flex;
	flex-wrap: wrap;
	justify-content: flex-end;
	align-items: center;
	gap: 6px;
}
/* Thin divider between the zoom-level tabs and the compact icon toggles. */
.map-tab-sep {
	width: 1px;
	align-self: stretch;
	margin: 2px 1px;
	background: var(--border);
}
.map-tab {
	font-family: "Courier New", monospace;
	font-size: 12px;
	letter-spacing: 1px;
	color: var(--accent);
	background: linear-gradient(165deg, var(--map-surface-hi), var(--map-surface-lo));
	border: 1px solid color-mix(in srgb, var(--accent) 32%, transparent);
	border-radius: 6px;
	padding: 5px 12px;
	cursor: pointer;
	box-shadow: inset 0 1px 0 var(--map-bevel-hi), inset 0 -2px 3px var(--map-bevel-lo), 0 2px 4px rgba(0, 0, 0, 0.22);
	transition: filter .12s, box-shadow .12s, transform .05s;
}
.map-tab:hover:not(:disabled) {
	filter: brightness(1.2);
}
.map-tab:active:not(:disabled) {
	transform: translateY(1px);
	box-shadow: inset 0 1px 3px var(--map-bevel-lo);
}
.map-tab.active {
	background: var(--accent);
	color: var(--bg);
	font-weight: bold;
	box-shadow: inset 0 1px 0 var(--map-bevel-hi), 0 0 10px color-mix(in srgb, var(--accent) 45%, transparent);
}
.map-tab:disabled {
	opacity: 0.4;
	cursor: default;
	border-color: var(--border);
	color: var(--text-dim);
}
/* Route (GPS) button: icon-only, same height as the text tabs but narrower.
   Lit cyan while armed (waiting for the next tile click); lit accent once a route
   is drawn (matches the .map-gps-line route), so a click there reads as "clear". */
.map-tab-icon {
	padding: 5px 9px;
	font-size: 15px;
	line-height: 1;
}
#map-route-toggle.has-route {
	background: var(--accent);
	border-color: var(--accent);
	color: var(--bg);
}
#map-body {
	display: flex;
	flex-direction: row;
	gap: 24px;
	align-items: flex-start;
}
/* Fixed 11-tile window of square tiles. The zone/interior grid is exactly 11×11 and
   fills the viewport edge-to-edge (gapless); the regional grid is larger — you drag
   to pan it. Sized to the tiles (11 × --map-room square) so the zone view fills the
   tube instead of floating in empty margins. */
#map-viewport {
	--map-room: 34px;
	width: calc(11 * var(--map-room));
	height: calc(11 * var(--map-room));
	overflow: hidden;
	position: relative;
	flex: 0 0 auto;
	cursor: grab;
	user-select: none;
	touch-action: none;
	/* Recessed CRT tube: rounded corners clip the grid like a bulged screen, and a
	   dark moulded bezel ring cradles it. The map underneath keeps its full colour. */
	border-radius: 10px;
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--bg2) 30%, #000 70%), 0 2px 8px rgba(0, 0, 0, 0.6);
}
#map-viewport.grabbing {
	cursor: grabbing;
}
/* Scanlines over the tube — faint, so tile colours read straight through. */
#map-viewport::before {
	content: '';
	position: absolute;
	inset: 0;
	z-index: 4;
	pointer-events: none;
	border-radius: inherit;
	background: repeating-linear-gradient(0deg, transparent 0 2px, rgba(0, 0, 0, 0.14) 2px 3px);
}
/* Curved-glass sheen + a soft corner vignette (only the extreme corners darken,
   so the map body stays full colour). Matches the tablet OS .mg-crt-glass. */
#map-viewport::after {
	content: '';
	position: absolute;
	inset: 0;
	z-index: 5;
	pointer-events: none;
	border-radius: inherit;
	background:
		linear-gradient(115deg, transparent 0 42%, rgba(220, 240, 255, 0.08) 47%, rgba(220, 240, 255, 0.02) 52%, transparent 60%),
		radial-gradient(120% 120% at 50% 50%, transparent 66%, rgba(0, 0, 0, 0.45) 100%);
}
#map-grid {
	--map-room: 34px;
	display: grid;
	justify-content: start;
	align-content: start;
	position: absolute;
	top: 0;
	left: 0;
	will-change: transform;
	font-family: "Courier New", monospace;
	font-size: 13px;
	line-height: 1;
}
.map-c {
	display: flex;
	align-items: center;
	justify-content: center;
	height: var(--map-room, 34px);
	white-space: pre;
}
.map-link {
	color: var(--text-dim);
}
/* Territory overlay layer — org-colour fill painted OVER the tile's own icons/
   labels (at 90% opacity, set inline) when the Territory view is on, so control
   dominates POI icons, abbreviations and the avenue-road overlay. pointer-events
   off so tile clicks still land. */
.map-terr-ov {
	position: absolute;
	inset: 0;
	z-index: 3;
	border-radius: inherit;
	pointer-events: none;
}
/* Drawn streets: a connector is a bar filling its gap cell, tinted (via inline
   --street) by the danger (zone) or land-use (regional) of the tiles it joins. */
.map-street::before {
	content: '';
	display: block;
	background: var(--street, var(--text-dim));
	border-radius: 1px;
}
.map-street-h::before { width: 100%; height: 2px; }
.map-street-v::before { width: 2px; height: 100%; }
/* Major road — a tagged (flags.artery) through-street, fixed colour regardless
   of danger/land-use tint, so the city's arteries read at a glance. */
.map-street-artery::before { background: var(--cyan); box-shadow: 0 0 3px var(--cyan); }
.map-street-h.map-street-artery::before { height: 3px; }
.map-street-v.map-street-artery::before { width: 3px; }
/* "Your exits" — streets touching the current tile, brightened. Wins over artery styling. */
.map-street-open::before { background: var(--accent); box-shadow: 0 0 4px var(--accent); }
.map-street-h.map-street-open::before { height: 3px; }
.map-street-v.map-street-open::before { width: 3px; }
/* Out-of-reach (beyond the regional reachability BFS): the tile and any street
   touching it fade back so you can see there's more city there you can't reach. */
.map-room.map-unreachable { opacity: 0.32; filter: grayscale(0.55); }
.map-street.map-dim::before, .map-link.map-dim { opacity: 0.25; }
.map-room {
	position: relative;
	font-weight: bold;
	border-radius: 2px;
	border: 1px solid transparent;
	color: var(--text);
}
/* Dead-end (a single connector) reads slightly muted. */
.map-room.map-deadend { opacity: 0.82; }

/* ── Avenue View: SVG road tiles + overlay ───────────────────────────────────
   In Avenue View the tile chrome is stripped to a clean arterial skeleton; each
   artery tile draws an SVG road (roadway band + dashed centreline) with arms
   toward its connected sides, so straight / corner / T / crossroads / dead-end /
   node all emerge and meet the gap-cell road bands seamlessly. An optional
   overlay (minimap icon or 2-letter abbrev) rides on top with a dark halo, so
   the road stays readable underneath. Current tile keeps an accent ring. */
#map-grid.avenue .map-room:not(.map-current) { background: none; border-color: transparent; }
#map-grid.avenue .map-room.map-current { border: 1px solid var(--accent); border-radius: 2px; background: none; }
#map-grid.avenue .av-gap { position: relative; }
/* White avenues at 90% opacity, with the label riding on top: full-white text
   with a solid black outline so it stays legible against the white roadway. */
.av-svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; pointer-events: none; opacity: 0.9; z-index: 1; }
.av-rd  { fill: none; stroke: #fff; stroke-width: 8; stroke-linecap: butt; stroke-linejoin: round; }
.av-hub { fill: #fff; }
.av-cl  { fill: none; stroke: var(--cyan); stroke-width: 1.3; stroke-dasharray: 2.2 2.6; stroke-linecap: butt; }
/* Route trace ("Route" toggle): a yellow road laid over the shortest walkable path
   from your tile to a clicked one — same geometry as the avenues, recoloured and
   drawn on top of whatever view is active so it reads as "follow this to get there". */
.tr-svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  pointer-events: none; z-index: 3; filter: drop-shadow(0 0 2px rgba(255,208,64,0.85)); }
.tr-rd  { fill: none; stroke: #ffd23f; stroke-width: 6; stroke-linecap: round; stroke-linejoin: round; }
.tr-hub { fill: #ffd23f; }
/* Full-map GPS route line — accent-coloured, laid over the grid through the route
   tiles' centres. stroke-width is set inline (scaled to the tile size per zoom). */
.map-gps-svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  pointer-events: none; z-index: 3; filter: drop-shadow(0 0 2px color-mix(in srgb, var(--accent) 80%, transparent)); }
.map-gps-line { fill: none; stroke: var(--accent); stroke-linecap: round; stroke-linejoin: round; }
/* The same route echoed on the sidebar/HUD/mobile minimaps. */
/* GPS route line on the sidebar minimap — an accent polyline spanning the grid's
   tracks (in tile-unit viewBox coords), matching the full-map + tablet route line. */
/* position:absolute keeps the SVG's containing block equal to the grid area it spans
   (via grid-column/row) while taking it OUT of grid auto-placement — otherwise, as a
   definitely-placed grid item it occupies all 9×9 tracks and shoves every tile cell
   into implicit rows below, splitting the line off above the map. */
.mm-gps-svg { position: absolute; grid-column: 1 / -1; grid-row: 1 / -1; width: 100%; height: 100%; pointer-events: none; z-index: 3; }
.mm-gps-line { fill: none; stroke: var(--accent); stroke-width: 0.18; stroke-linecap: round; stroke-linejoin: round; }
/* Overlay glyph / abbrev ON TOP of the road. Icons ride bare (keep their POI
   colour); letters are white with a thin black outline for legibility. */
.av-ov { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; z-index: 2; pointer-events: none; }
.av-ov-icon  { font-size: 12px; font-weight: bold; }
.av-ov-label { font-size: 11px; font-weight: bold; letter-spacing: -0.5px; color: #fff;
  -webkit-text-stroke: 0.6px #000; paint-order: stroke fill; }

/* Building overlay (labels / icons mode): a 2-letter acronym or a building-type
   glyph painted OVER the building's SVG rooftop footprint, so the base map art
   stays visible underneath. Absolutely centred over the tile; a dark halo keeps
   it legible against any footprint colour. */
.map-bld-ov {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 2;
	pointer-events: none;
	font-weight: bold;
}
/* Labels overlay: a big 2-letter acronym that replaces the building graphic and fills
   the tile. White with a heavy black outline so it reads clearly over any tile colour.
   Sized per surface (the full-map tiles are larger than the sidebar's). */
.map-bld-label {
	font-size: 13px;
	font-weight: 800;
	line-height: 1;
	letter-spacing: -1px;
	color: #fff;
	-webkit-text-stroke: 0.8px #000;
	paint-order: stroke fill;
}
#map-grid .map-bld-label { font-size: 20px; letter-spacing: -1.5px; -webkit-text-stroke: 1px #000; }
/* Sidebar/HUD/mobile minimaps: smaller tiles, so a smaller 2-letter label that still
   fills the square. */
/* Sized off --mm-room (the zoom-driven tile size set by applyMinimapZoom) rather
   than a fixed px, so the 2-letter code keeps filling its square at every zoom. */
#minimap-grid .map-bld-label,
#minimap-grid-hud .map-bld-label,
#minimap-grid-mob .map-bld-label {
	font-size: calc(var(--mm-room) * 0.7);
	letter-spacing: calc(var(--mm-room) * -0.03);
	-webkit-text-stroke: calc(var(--mm-room) * 0.035) #000;
}

/* ── Tileable terrain (server `terrain` field: road / water / grass) ──────────
   Shared by the sidebar minimap (.mm-*) and the full-map popup (.map-*). The fill
   colour is set inline (grey asphalt for roads, the authored blue/green for water/
   grass); these classes drop the tile border + rounding so a body of water or a
   park reads as one continuous surface, and lay a subtle SVG texture that lines up
   edge-to-edge across neighbouring tiles (one period per tile, matching phase at
   the shared edges). Roads carry no texture — their grey surface + the yellow
   dashed SVG mask (currentColor) are the whole read. */
.mm-terr, .map-terr { border: 0 !important; border-radius: 0 !important; }
.mm-water, .map-water, .mm-grass, .map-grass, .mm-dock, .map-dock,
.mm-scrub, .map-scrub, .mm-redrock, .map-redrock, .mm-ash, .map-ash, .mm-marsh, .map-marsh { background-repeat: no-repeat; background-size: 100% 100%; }
.mm-water, .map-water {
	background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'><g fill='none' stroke='%23ffffff' stroke-opacity='0.30' stroke-width='1.1' stroke-linecap='round'><path d='M0 7q6 -3 12 0t12 0'/><path d='M0 14q6 -3 12 0t12 0'/><path d='M0 21q6 -3 12 0t12 0'/></g></svg>");
}
.mm-grass, .map-grass {
	background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'><g fill='none' stroke='%237fc95a' stroke-opacity='0.40' stroke-width='1' stroke-linecap='round'><path d='M4 21v-5'/><path d='M9 22v-6'/><path d='M14 21v-5'/><path d='M19 22v-6'/><path d='M6 13v-4'/><path d='M12 12v-4'/><path d='M18 13v-4'/></g></svg>");
}
/* Dock/pier: wooden decking — dark plank seams with staggered butt-joints + faint grain. */
.mm-dock, .map-dock {
	background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'><g fill='none' stroke='%233b2c19' stroke-opacity='0.55' stroke-width='1'><path d='M0 6h24M0 12h24M0 18h24'/><path d='M8 0v6M16 6v6M8 12v6M16 18v6'/></g><g fill='none' stroke='%23987444' stroke-opacity='0.30' stroke-width='0.6'><path d='M0 3h24M0 9h24M0 15h24M0 21h24'/></g></svg>");
}
/* --- Post-apocalyptic wildlands surfaces (the wilds beyond the Curtain) --- */
/* Scrubland: sparse dry brush tufts + a few scattered pebbles over dusty olive. */
.mm-scrub, .map-scrub {
	background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'><g fill='none' stroke='%23b5b06a' stroke-opacity='0.45' stroke-width='0.9' stroke-linecap='round'><path d='M5 20l-1.5-4M5 20l1.5-4M5 20v-5'/><path d='M17 22l-1.5-4M17 22l1.5-4M17 22v-5'/><path d='M11 14l-1-3M11 14l1-3'/></g><g fill='%23807a40' fill-opacity='0.4'><circle cx='9' cy='20' r='0.9'/><circle cx='20' cy='11' r='0.8'/><circle cx='3' cy='8' r='0.7'/></g></svg>");
}
/* Red rock: angular rust facets + darker cracks, littered with rust grit,
   little boulders + pale flecks — mesa/badland stone strewn with debris. */
.mm-redrock, .map-redrock {
	background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'><g fill='none' stroke='%23431c10' stroke-opacity='0.42' stroke-width='0.9'><path d='M0 9l7 3 6-4 5 4 6-2'/><path d='M4 24l3-8 6 2 4-6'/></g><g fill='%233a170c' fill-opacity='0.45'><circle cx='3' cy='19' r='1.2'/><circle cx='15' cy='7' r='1'/><circle cx='20' cy='18' r='0.9'/><circle cx='9' cy='4' r='0.7'/><circle cx='22' cy='3' r='0.6'/></g></svg>");
}
/* Ash: fine pale flecks + a drifting wisp over burnt grey. */
.mm-ash, .map-ash {
	background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'><g fill='%23cfcac4' fill-opacity='0.35'><circle cx='4' cy='6' r='0.8'/><circle cx='12' cy='10' r='0.7'/><circle cx='19' cy='5' r='0.9'/><circle cx='8' cy='17' r='0.7'/><circle cx='16' cy='19' r='0.8'/><circle cx='21' cy='14' r='0.6'/></g><path d='M2 21q6 -3 11 0t9 -1' fill='none' stroke='%23b8b2ac' stroke-opacity='0.2' stroke-width='0.8'/></svg>");
}
/* Marsh: murky ripples + a couple of reed blades — toxic wetland. */
.mm-marsh, .map-marsh {
	background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'><g fill='none' stroke='%23aeca7e' stroke-opacity='0.28' stroke-width='1' stroke-linecap='round'><path d='M0 8q6 -3 12 0t12 0'/><path d='M0 16q6 -3 12 0t12 0'/></g><g fill='none' stroke='%236f8a3e' stroke-opacity='0.5' stroke-width='0.9' stroke-linecap='round'><path d='M7 20v-7M9 20l-1-6'/><path d='M18 21v-8'/></g></svg>");
}

/* Entrance arrow — a small amber triangle on the edge of a building tile pointing to
   the side the door faces (server `entrance` field). CSS triangle so it stays crisp
   at any tile size; a dark halo keeps it readable over a rooftop footprint. Shared by
   the sidebar minimap (.mm-*) and the full-map popup (.map-*). */
.mm-entrance, .map-entrance { position: absolute; width: 0; height: 0; z-index: 3; pointer-events: none; filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.95)); }
.mm-ent-north, .map-ent-north { top: 0; left: 50%; transform: translateX(-50%); border-left: 4px solid transparent; border-right: 4px solid transparent; border-bottom: 6px solid var(--accent); }
.mm-ent-south, .map-ent-south { bottom: 0; left: 50%; transform: translateX(-50%); border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 6px solid var(--accent); }
.mm-ent-east, .map-ent-east { right: 0; top: 50%; transform: translateY(-50%); border-top: 4px solid transparent; border-bottom: 4px solid transparent; border-left: 6px solid var(--accent); }
.mm-ent-west, .map-ent-west { left: 0; top: 50%; transform: translateY(-50%); border-top: 4px solid transparent; border-bottom: 4px solid transparent; border-right: 6px solid var(--accent); }

/* (The None / Icons / Labels segmented slider that used to sit in the retired
   full-screen map header is gone with it — the mode is a Settings pill now, and
   Icons isn't a mode any more. Nothing rendered .map-overlay-slider.) */

/* Mobile: the header packs the title, level tabs, GPS/territory icons and the
   overlay slider into one row, and the body's viewport is a fixed 374px — wider
   than a phone. That forced the box to scroll sideways, sliding the right-hand
   buttons off-screen. Wrap the header, stack the body, and shrink the tiles so
   every control stays reachable without a horizontal scrollbar. */
@media (max-width: 720px) {
  #map-box { max-width: 96vw; padding: 12px; }
  #map-header { flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
  #map-header h2 { flex: 1 1 100%; margin-bottom: 2px; }
  #map-tabs { justify-content: flex-start; flex: 1 1 auto; }
  #map-body { flex-direction: column; align-items: center; gap: 14px; }
  #map-viewport, #map-grid { --map-room: 28px; }
  #map-legend { width: 100%; }
}

/* Landmark POI icons — bold, colour-coded by category (airport, police, …). */
.map-poi { font-weight: bold; }
.map-poi-aa      { color: #ff5a4a; }
.map-poi-airport { color: #2fd0c0; }
.map-poi-police  { color: #6a9bff; }
.map-poi-power   { color: #ffd23a; }
.map-poi-club    { color: #ff6ac1; }
.map-poi-hotel   { color: #e0b56a; }
.map-poi-bar     { color: #f0a838; }
.map-poi-vendor  { color: #4bd07a; }
.map-poi-home    { color: #c9a884; }
.map-poi-stairs  { color: #c0a0ff; }
.map-room.danger-safe {
	background: rgba(35, 100, 55, 0.45);
	border-color: rgba(80, 180, 100, 0.55);
}
.map-room.danger-low {
	background: rgba(115, 100, 20, 0.45);
	border-color: rgba(210, 190, 60, 0.55);
}
.map-room.danger-medium {
	background: rgba(125, 75, 20, 0.5);
	border-color: rgba(220, 140, 50, 0.6);
}
.map-room.danger-high,
.map-room.danger-lethal {
	background: rgba(115, 30, 30, 0.5);
	border-color: rgba(200, 60, 60, 0.6);
}
.map-room.map-styled {
	background: none;
	border-color: transparent;
}
.map-room.map-current {
	color: var(--accent);
	background: none;
}

#map-legend {
	font-family: "Courier New", monospace;
	font-size: 12px;
	color: var(--text);
	flex: 0 0 auto;
	min-width: 220px;
	max-width: 300px;
	/* Match the 11-tile map viewport height (11 × --map-room square) so the panel
	   bottom lines up with the map, and clip/scroll anything past it (regional's
	   land-use list can be tall) so it never bleeds over the CLOSE button. */
	max-height: calc(11 * 34px);
	overflow: hidden auto;
	scrollbar-width: thin;
	scrollbar-color: var(--accent) var(--bg);
	display: flex;
	flex-direction: column;
	align-content: flex-start;
	gap: 6px;
	/* Raised tablet panel (matches the chassis buttons) instead of a bare divider. */
	padding: 12px 14px;
	background: linear-gradient(165deg, var(--map-surface-hi), var(--map-surface-lo));
	border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
	border-radius: 10px;
	box-shadow: inset 0 1px 0 var(--map-bevel-hi), inset 0 -2px 3px var(--map-bevel-lo), 0 2px 5px rgba(0, 0, 0, 0.22);
}
.map-leg-head {
	color: var(--accent);
	padding-bottom: 6px;
	border-bottom: 1px solid var(--border);
}
/* Alphabetical room list (interior/zone views) — wraps into columns rather
   than scrolling when it outgrows the map's height. */
.map-list {
	display: flex;
	flex-direction: column;
	gap: 2px;
	flex: 1 1 auto;
	min-height: 0;
	/* Vertical scroll only — the explicit `hidden` on the x-axis stops the
	   overflow-y:auto quirk from also spawning a horizontal scrollbar. */
	overflow: hidden auto;
	scrollbar-gutter: stable;
	scrollbar-width: thin;
	scrollbar-color: var(--accent) var(--bg);
	padding-right: 4px;
}
.map-list-row {
	cursor: pointer;
	padding: 0 3px;
	border-radius: 2px;
	line-height: 1.4;
	white-space: nowrap;
}
.map-list .map-leg-sym {
	min-width: 20px;
	padding: 0 2px;
}
.map-list-row:hover {
	background: var(--bg);
}
.map-list-current {
	color: var(--accent);
}
/* Legend-click selection: the building the player clicked in the list, highlighted
   in accent both in the list row and (via .map-legend-sel) on the map tile. */
.map-list-sel {
	background: color-mix(in srgb, var(--accent) 26%, transparent);
	color: var(--accent);
	font-weight: 700;
}
.map-room.map-legend-sel {
	z-index: 4;
	box-shadow: inset 0 0 0 2px var(--accent), 0 0 9px 1px var(--accent);
}
.map-leg-row {
	display: flex;
	align-items: center;
	gap: 8px;
	white-space: nowrap;
	flex: 0 0 auto;
}
#map-tooltip {
	display: none;
	position: fixed;
	/* Above #map-panel (z-index 9250) so map hover tips aren't hidden behind the map. */
	z-index: 9300;
	max-width: 300px;
	background: var(--bg2);
	border: 1px solid var(--accent);
	border-radius: 2px;
	padding: 8px 10px;
	font-family: "Courier New", monospace;
	font-size: 12px;
	color: var(--text);
	pointer-events: none;
	box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
}
#map-tooltip .map-tt-name {
	color: var(--accent);
	font-weight: bold;
	margin-bottom: 4px;
}
#map-tooltip .map-tt-district {
	font-size: 11px;
	font-style: italic;
	margin-bottom: 4px;
}
#map-tooltip .map-tt-desc {
	color: var(--text-dim);
	white-space: normal;
	margin-bottom: 4px;
}
#map-tooltip .map-tt-bld {
	white-space: normal;
}
.map-leg-sym {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	position: relative;
	min-width: 22px;
	padding: 1px 3px;
	font-weight: bold;
	border-radius: 2px;
	white-space: pre;
}
/* Legend glyph keys: street bar, your-exits bar, building dot. */
.map-leg-street {
	display: inline-block;
	width: 14px;
	height: 2px;
	border-radius: 1px;
	background: var(--text-dim);
	vertical-align: middle;
}
.map-leg-street.map-leg-street-artery {
	height: 3px;
	background: var(--cyan);
	box-shadow: 0 0 3px var(--cyan);
}
.map-leg-street.map-leg-street-open {
	height: 3px;
	background: var(--accent);
	box-shadow: 0 0 3px var(--accent);
}
.map-leg-sym.danger-safe {
	background: rgba(35, 100, 55, 0.45);
	border: 1px solid rgba(80, 180, 100, 0.55);
}
.map-leg-sym.danger-low {
	background: rgba(115, 100, 20, 0.45);
	border: 1px solid rgba(210, 190, 60, 0.55);
}
.map-leg-sym.danger-medium {
	background: rgba(125, 75, 20, 0.5);
	border: 1px solid rgba(220, 140, 50, 0.6);
}
.map-leg-sym.danger-high,
.map-leg-sym.danger-lethal {
	background: rgba(115, 30, 30, 0.5);
	border: 1px solid rgba(200, 60, 60, 0.6);
}
.map-leg-sym.map-styled {
	background: none;
}
.map-leg-sym.map-current {
	color: var(--accent);
	background: none;
}

/* Recipes panel */
#recipes-panel {
	display: none;
	position: fixed;
	inset: 0;
	background: transparent;
	z-index: 150;
	align-items: center;
	justify-content: center;
}
#recipes-panel.active {
	display: flex;
}
#recipes-box {
	background: var(--bg2);
	border: 1px solid var(--accent);
	padding: 20px;
	max-width: 620px;
	width: 94%;
	border-radius: 2px;
	max-height: 88vh;
	display: flex;
	flex-direction: column;
}
#recipes-header {
	display: flex;
	justify-content: space-between;
	align-items: center;
	margin-bottom: 14px;
}
#recipes-header h2 {
	color: var(--accent);
	font-size: 14px;
	letter-spacing: 2px;
	text-transform: uppercase;
}
#recipes-close {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	width: 26px;
	height: 26px;
	cursor: pointer;
	border-radius: 2px;
}
#recipes-close:hover {
	border-color: var(--red);
	color: var(--red);
}

#recipes-tabs {
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
	margin-bottom: 12px;
}
.recipe-tab {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	padding: 4px 10px;
	cursor: pointer;
	border-radius: 2px;
	font-size: 10px;
	text-transform: uppercase;
	letter-spacing: 1px;
}
.recipe-tab:hover {
	border-color: var(--accent);
	color: var(--accent);
}
.recipe-tab.active {
	border-color: var(--accent);
	color: var(--accent);
	background: var(--bg);
}

#recipes-list {
	flex: 1;
	overflow-y: auto;
	min-height: 0;
}
.recipes-empty {
	color: var(--text-dim);
	font-style: italic;
	font-size: 12px;
	padding: 8px;
}

.recipe-card {
	position: relative;
	border: 1px solid var(--border);
	background: var(--bg3);
	padding: 10px 12px;
	margin-bottom: 8px;
	border-radius: 2px;
}
.recipe-name {
	color: var(--accent);
	font-weight: bold;
	font-size: 13px;
	margin-bottom: 4px;
}
.recipe-desc {
	color: var(--text);
	font-size: 11px;
	margin-bottom: 6px;
}
.recipe-output {
	color: var(--cyan);
	font-size: 11px;
	margin-bottom: 4px;
}
.recipe-station {
	color: var(--text-dim);
	font-size: 10px;
	margin-bottom: 4px;
}
.recipe-ings {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin-top: 4px;
}
.recipe-ing {
	font-size: 10px;
	color: var(--text-dim);
}
.recipe-ing.ok {
	color: var(--green);
}
.recipe-ing.missing {
	color: var(--red);
}

.recipe-craft-btn {
	position: absolute;
	top: 10px;
	right: 12px;
	background: transparent;
	border: 1px solid var(--accent);
	color: var(--accent);
	padding: 4px 12px;
	cursor: pointer;
	border-radius: 2px;
	font-size: 11px;
	text-transform: uppercase;
	letter-spacing: 1px;
}
.recipe-craft-btn:hover:not(:disabled) {
	background: var(--accent);
	color: var(--bg);
}
.recipe-craft-btn:disabled {
	opacity: 0.35;
	cursor: not-allowed;
	border-color: var(--border);
	color: var(--text-dim);
}

#recipes-pager {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 12px;
	margin-top: 12px;
}
#recipes-pager:empty {
	display: none;
}
.recipe-page-btn {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	padding: 4px 10px;
	cursor: pointer;
	border-radius: 2px;
	font-size: 11px;
}
.recipe-page-btn:hover:not(:disabled) {
	border-color: var(--accent);
	color: var(--accent);
}
.recipe-page-btn:disabled {
	opacity: 0.3;
	cursor: default;
}
.recipe-page-label {
	font-size: 10px;
	color: var(--text-dim);
	letter-spacing: 1px;
}

/* Stats panel */
#stats-panel {
	display: none;
	position: fixed;
	inset: 0;
	background: transparent;
	z-index: 150;
	align-items: center;
	justify-content: center;
}
#stats-panel.active {
	display: flex;
}
#stats-box {
	background: var(--bg2);
	border: 1px solid var(--accent);
	padding: 20px;
	max-width: 640px;
	width: 94%;
	border-radius: 2px;
	max-height: 88vh;
	display: flex;
	flex-direction: column;
}
.stats-cols {
	display: flex;
	gap: 24px;
}
.stats-col {
	flex: 1;
	min-width: 0;
}
#stats-header {
	display: flex;
	justify-content: space-between;
	align-items: center;
	margin-bottom: 14px;
}
#stats-header h2 {
	color: var(--accent);
	font-size: 14px;
	letter-spacing: 2px;
	text-transform: uppercase;
}
#stats-close {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	width: 26px;
	height: 26px;
	cursor: pointer;
	border-radius: 2px;
}
#stats-close:hover {
	border-color: var(--red);
	color: var(--red);
}
#stats-body {
	overflow-y: auto;
}
.stats-title {
	color: var(--accent);
	font-weight: bold;
	text-transform: uppercase;
	letter-spacing: 2px;
	padding-bottom: 14px;
	margin-bottom: 14px;
	border-bottom: 1px solid var(--border);
}
.stats-handle {
	color: var(--cyan);
}
.stats-section {
	border-top: 1px solid var(--border);
	padding: 10px 0;
}
.stats-section:first-of-type {
	border-top: none;
	padding-top: 0;
}
.stats-row {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 10px;
	padding: 2px 0;
}
.stats-label {
	color: var(--accent);
	text-transform: uppercase;
	letter-spacing: 1px;
	font-size: 11px;
}
.stats-val {
	color: var(--text);
	margin-left: auto;
}
.stats-note {
	color: var(--text-dim);
	font-size: 11px;
	margin-bottom: 8px;
}
.stats-note b {
	color: var(--accent);
}
.stats-raise-btn {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--accent);
	width: 22px;
	height: 22px;
	line-height: 1;
	cursor: pointer;
	border-radius: 2px;
	flex: none;
}
.stats-raise-btn:hover:not(:disabled) {
	border-color: var(--accent);
	background: var(--bg);
}
.stats-raise-btn:disabled {
	opacity: 0.3;
	cursor: default;
}
.stats-cat {
	color: var(--accent);
	text-transform: uppercase;
	letter-spacing: 1px;
	font-size: 10px;
	margin: 8px 0 2px;
}
.stats-cat:first-child {
	margin-top: 0;
}
.stats-stat-block {
	margin-bottom: 6px;
}
.stats-desc {
	color: var(--text-dim);
	font-size: 10px;
	font-style: italic;
}
.stats-skill-name {
	color: var(--text);
}
.stats-skill-gov {
	color: var(--text-dim);
	font-size: 9px;
	letter-spacing: 1px;
	margin-left: auto;
	padding-right: 8px;
}
.stats-skill .stats-val {
	margin-left: 0;
	min-width: 1.5em;
	text-align: right;
}
#stats-body .status-flags {
	margin-top: 10px;
	color: var(--cyan);
	font-size: 12px;
}

/* SKILLS PANEL */
#skills-panel {
	display: none;
	position: fixed;
	inset: 0;
	background: transparent;
	z-index: 150;
	align-items: center;
	justify-content: center;
}
#skills-panel.active {
	display: flex;
}
#skills-box {
	background: var(--bg2);
	border: 1px solid var(--accent);
	padding: 20px;
	max-width: 680px;
	width: 94%;
	border-radius: 2px;
	max-height: 88vh;
	display: flex;
	flex-direction: column;
}
#skills-header {
	display: flex;
	justify-content: space-between;
	align-items: center;
	margin-bottom: 14px;
}
#skills-header h2 {
	color: var(--accent);
	font-size: 14px;
	letter-spacing: 2px;
	text-transform: uppercase;
}
#skills-close {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	width: 26px;
	height: 26px;
	cursor: pointer;
	border-radius: 2px;
}
#skills-close:hover {
	border-color: var(--red);
	color: var(--red);
}
#skills-body {
	overflow-y: auto;
	padding-right: 10px;
}
#skills-body::-webkit-scrollbar {
	width: 6px;
}
#skills-body::-webkit-scrollbar-track {
	background: var(--bg2);
}
#skills-body::-webkit-scrollbar-thumb {
	background: var(--border);
	border-radius: 3px;
}
.skills-note {
	color: var(--text-dim);
	font-size: 12px;
	margin-bottom: 14px;
}
.skills-section {
	border-top: 1px solid var(--border);
	padding: 10px 0;
}
.skills-section:first-of-type {
	border-top: none;
	padding-top: 0;
}
.skills-cat {
	color: var(--accent);
	text-transform: uppercase;
	letter-spacing: 2px;
	font-size: 12px;
	font-weight: bold;
	margin-bottom: 6px;
}
.skills-block {
	padding: 6px 0;
	border-top: 1px dotted var(--border);
}
.skills-block:first-of-type {
	border-top: none;
}
.skills-skill-row {
	display: flex;
	align-items: center;
	gap: 8px;
}
.skills-skill-name {
	flex: 1;
	min-width: 0;
	color: var(--text);
}
.skills-col {
	width: 56px;
	flex: none;
	text-align: center;
	font-variant-numeric: tabular-nums;
	color: var(--text-dim);
}
.skills-final {
	color: var(--cyan);
	font-weight: bold;
}
.skills-colhead .skills-skill-name,
.skills-colhead .skills-col {
	color: var(--accent);
	text-transform: uppercase;
	letter-spacing: 1px;
	font-size: 10px;
}
.skills-gov {
	color: var(--accent);
	font-size: 10px;
	letter-spacing: 1px;
}
.skills-desc {
	color: var(--text-dim);
	font-size: 10px;
	font-style: italic;
	margin-top: 2px;
}

@media (max-width: 720px) {
	.stats-cols {
		flex-direction: column;
		gap: 0;
	}
	#equip-body {
		flex-direction: column;
		overflow-y: auto;
	}
	#equip-backpack-col {
		width: 100%;
		overflow: visible;
	}
}

/* ── Container Panel ── */
#container-panel {
	display: none;
	position: fixed;
	inset: 0;
	background: transparent;
	z-index: 150;
	align-items: center;
	justify-content: center;
}
#container-panel.active {
	display: flex;
}
#loot-panel {
	display: none;
	position: fixed;
	inset: 0;
	background: transparent;
	z-index: 150;
	align-items: center;
	justify-content: center;
}
#loot-panel.active {
	display: flex;
}
#loot-box {
	background: var(--bg2);
	border: 1px solid var(--cyan);
	padding: 20px;
	width: 94%;
	max-width: 720px;
	border-radius: 2px;
	height: 80vh;
	max-height: 88vh;
	display: flex;
	flex-direction: column;
	gap: 12px;
}
#loot-header {
	display: flex;
	justify-content: space-between;
	align-items: center;
}
#loot-header h2 {
	color: var(--cyan);
	font-size: 14px;
	letter-spacing: 2px;
	text-transform: uppercase;
	margin: 0;
}
#loot-body {
	display: flex;
	gap: 0;
	overflow: hidden;
	flex: 1;
	min-height: 0;
}
#loot-divider {
	flex-shrink: 0;
	display: flex;
	align-items: center;
	font-size: 18px;
	color: var(--text-dim);
	padding: 0 4px;
}
#loot-contents-col {
	padding-left: 14px;
}
.loot-col-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
}
#loot-transfer-all {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-size: 13px;
	line-height: 1;
	padding: 1px 5px;
	cursor: pointer;
	border-radius: 2px;
	flex-shrink: 0;
}
#loot-transfer-all:hover {
	border-color: var(--green);
	color: var(--green);
}
#loot-inv-list {
	border: 1px dashed var(--border);
	border-radius: 3px;
}
#loot-inv-list,
#loot-contents-list {
	flex: 1;
	overflow-y: auto;
	min-height: 80px;
	display: flex;
	flex-direction: column;
	gap: 6px;
	padding: 4px;
	border: 1px dashed transparent;
	border-radius: 3px;
}
#loot-inv-list.ctr-drag-over {
	border-color: var(--cyan);
	background: rgba(40, 229, 255, 0.05);
}
#loot-notify {
	font-size: 11px;
	color: var(--yellow, #c8a000);
	text-align: center;
	min-height: 14px;
}
#loot-close {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	width: 26px;
	height: 26px;
	cursor: pointer;
	border-radius: 2px;
}
#loot-close:hover {
	border-color: var(--red);
	color: var(--red);
}
#loot-footer {
	display: flex;
	justify-content: flex-end;
	gap: 8px;
}
.loot-btn {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-size: 10px;
	letter-spacing: 1px;
	text-transform: uppercase;
	padding: 6px 14px;
	cursor: pointer;
	border-radius: 2px;
}
.loot-btn:hover {
	border-color: var(--cyan);
	color: var(--cyan);
}
#loot-butcher {
	border-color: var(--red);
	color: var(--red);
}
#loot-butcher:hover {
	border-color: var(--red);
	color: var(--text);
	background: var(--red);
}
.loot-btn-take-all {
	border-color: var(--green);
	color: var(--green);
}
.loot-btn-take-all:hover {
	border-color: var(--green);
	color: var(--bg);
	background: var(--green);
}
.loot-btn-stow-all {
	border-color: var(--cyan);
	color: var(--cyan);
}
.loot-btn-stow-all:hover {
	border-color: var(--cyan);
	color: var(--bg);
	background: var(--cyan);
}
#container-footer {
	display: flex;
	justify-content: flex-end;
	gap: 8px;
}
#container-box {
	background: var(--bg2);
	border: 1px solid var(--cyan);
	padding: 20px;
	width: 94%;
	max-width: 720px;
	border-radius: 2px;
	height: 80vh;
	max-height: 88vh;
	display: flex;
	flex-direction: column;
	gap: 12px;
	position: relative;
	overflow: hidden;
	/* Own stacking context, so the light-spill layer (z-index -1) paints above
	   the panel's background instead of vanishing behind it. */
	isolation: isolate;
	transition: background 0.2s, border-color 0.2s, box-shadow 0.2s;
}

/* Cold-appliance theming — applied only when the opened container (or its
   paired box) carries a `preserves` tag, i.e. an actual fridge/freezer, not
   a crate/bag/trash bin. Two grades per the fridge/freezer redesign:
   consumer (frosted glass, apartment units) vs. commercial (brushed
   industrial steel, Ration Nine's Ironchill line). */
#container-box.ctr-theme-consumer {
	/* A domestic unit is finished in the player's own palette: every colour
	   below derives from the theme's cool channel (--cyan), so a Void fridge
	   is icy blue and an Ember one is warm brass without a second ruleset.
	   Only genuine light — speculars, condensation, the interior lamp — stays
	   white, because light isn't a finish. Commercial steel is deliberately
	   NOT themed: Ironchill is a brand, and it looks the same in every kitchen.

	   The unit's FINISH is the theme colour, undiluted — door face, casing,
	   handle metal, shelf lips, LEDs, the light it throws. Blue-white is kept
	   for the things that are physically blue-white whatever the cabinet is
	   painted: frost and ice crystals, condensation beads, speculars. */
	--ctr-cold: var(--cyan);
	--ctr-accent: var(--cyan);
	--ctr-frost: #cfeeff;
	--ctr-shelf-lip: color-mix(in srgb, var(--ctr-cold) 55%, transparent);
	--ctr-card-bg: linear-gradient(180deg, color-mix(in srgb, var(--ctr-cold) 9%, transparent), rgba(6, 14, 20, 0.44));
	--ctr-card-border: color-mix(in srgb, var(--ctr-cold) 24%, transparent);
	background:
		radial-gradient(120% 60% at 50% -10%, color-mix(in srgb, var(--ctr-cold) 20%, transparent), transparent 70%),
		linear-gradient(165deg, color-mix(in srgb, var(--ctr-cold) 12%, var(--bg2)), var(--bg2) 55%, color-mix(in srgb, var(--ctr-cold) 5%, var(--bg2)));
	border: 1px solid color-mix(in srgb, var(--ctr-cold) 55%, var(--border));
	box-shadow:
		inset 0 1px 0 color-mix(in srgb, white 25%, transparent),
		0 0 28px color-mix(in srgb, var(--ctr-cold) 16%, transparent);
}
#container-box.ctr-theme-consumer #container-header h2,
#container-box.ctr-theme-consumer .container-section-label {
	color: var(--ctr-cold);
	text-shadow: 0 0 6px color-mix(in srgb, var(--ctr-cold) 50%, transparent);
}

#container-box.ctr-theme-commercial {
	--ctr-steel-hi: #6b7176;
	--ctr-steel-lo: #24272a;
	--ctr-hazard: #e8b84b;
	--ctr-accent: #e8b84b;
	--ctr-frost: #cfeeff;
	--ctr-shelf-lip: rgba(222, 230, 235, 0.42);
	--ctr-card-bg: linear-gradient(180deg, rgba(255, 255, 255, 0.055), rgba(0, 0, 0, 0.4));
	--ctr-card-border: rgba(214, 220, 224, 0.2);
	background:
		repeating-linear-gradient(115deg, rgba(255, 255, 255, 0.025) 0 2px, transparent 2px 6px),
		linear-gradient(160deg, color-mix(in srgb, var(--ctr-steel-hi) 35%, var(--bg2)), var(--bg2) 55%, var(--ctr-steel-lo));
	border: 2px solid var(--ctr-steel-hi);
	box-shadow:
		inset 0 1px 0 rgba(255, 255, 255, 0.08),
		inset 0 0 0 1px rgba(0, 0, 0, 0.5),
		0 10px 30px rgba(0, 0, 0, 0.5);
}
#container-box.ctr-theme-commercial #container-header h2 {
	color: var(--ctr-hazard);
	letter-spacing: 3px;
	text-shadow: none;
}
#container-box.ctr-theme-commercial .container-section-label {
	color: color-mix(in srgb, var(--ctr-hazard) 55%, var(--text-dim));
}
#container-box.ctr-theme-commercial #container-inv-col {
	border-right-color: var(--ctr-steel-hi);
}

/* Freezer sub-box: its own smaller door within the appliance body, layered
   on top of whichever grade theme the panel is in — a freezer reads icy
   regardless of whether the unit around it is domestic or industrial. */
.ctr-subbox.ctr-frost {
	position: relative;
	padding-left: 20px;
	border-radius: 10px;
	border: none;
	background:
		linear-gradient(178deg,
			color-mix(in srgb, var(--ctr-cold, #bfe9ff) 12%, transparent),
			transparent 40%,
			rgba(0, 0, 0, 0.22));
	box-shadow:
		/* the same machined ring as the cabinet, one size down */
		0 1px 0 1px color-mix(in srgb, var(--ctr-cold, #bfe9ff) 40%, #ffffff),
		0 0 0 2px color-mix(in srgb, var(--ctr-cold, #bfe9ff) 40%, #262c31),
		0 2px 0 2px rgba(0, 0, 0, 0.45),
		inset 0 0 0 1px rgba(255, 255, 255, 0.08),
		inset 0 2px 14px rgba(0, 0, 0, 0.5),
		0 8px 20px -10px rgba(0, 0, 0, 0.7);
}
.ctr-subbox.ctr-frost .container-section-label {
	color: color-mix(in srgb, var(--ctr-cold, #bfe9ff) 75%, #ffffff);
	text-shadow: 0 0 7px color-mix(in srgb, var(--ctr-cold, #bfe9ff) 55%, transparent);
}
/* Its own touch rail, matching the cabinet's — no handle, no hardware. */
.ctr-subbox.ctr-frost::before {
	content: "";
	position: absolute;
	left: 7px;
	top: 12px;
	bottom: 12px;
	width: 3px;
	border-radius: 2px;
	background:
		linear-gradient(180deg, transparent, rgba(255, 255, 255, 0.85) 45% 55%, transparent) no-repeat,
		linear-gradient(180deg,
			color-mix(in srgb, var(--ctr-cold, #bfe9ff) 25%, transparent),
			var(--ctr-cold, #bfe9ff) 28% 72%,
			color-mix(in srgb, var(--ctr-cold, #bfe9ff) 25%, transparent));
	background-size: 100% 20%, auto;
	background-position: 0 -30%, 0 0;
	box-shadow:
		0 0 9px color-mix(in srgb, var(--ctr-cold, #bfe9ff) 55%, transparent),
		inset 0 0 0 1px rgba(0, 0, 0, 0.35);
	animation: ctr-glint 9s ease-in-out infinite;
	z-index: 2;
}
/* A highlight sliding the length of a chrome tube, with long dead time between
   passes so it reads as ambient, not as a loading bar. */
@keyframes ctr-glint {
	0%, 62% { background-position: 0 -30%, 0 0; }
	88%, 100% { background-position: 0 130%, 0 0; }
}
/* Rime creeping in from the corners — irregular blooms plus a few crystal
   spurs, densest at the corners and thinning toward the middle. */
.ctr-subbox.ctr-frost::after {
	content: "";
	position: absolute;
	inset: 0;
	pointer-events: none;
	border-radius: 10px;
	background-image:
		/* corner blooms */
		radial-gradient(ellipse 46px 26px at 100% 0%, rgba(238, 250, 255, 0.34), transparent 72%),
		radial-gradient(ellipse 34px 20px at 0% 100%, rgba(238, 250, 255, 0.26), transparent 72%),
		radial-gradient(ellipse 26px 16px at 100% 100%, rgba(238, 250, 255, 0.2), transparent 74%),
		/* scattered crystal clusters */
		radial-gradient(circle at 86% 30%, rgba(255, 255, 255, 0.42) 0 1.3px, transparent 2.4px),
		radial-gradient(circle at 92% 52%, rgba(255, 255, 255, 0.34) 0 1px, transparent 2px),
		radial-gradient(circle at 78% 12%, rgba(255, 255, 255, 0.46) 0 1.6px, transparent 2.8px),
		radial-gradient(circle at 12% 84%, rgba(255, 255, 255, 0.36) 0 1.2px, transparent 2.2px),
		radial-gradient(circle at 5% 62%, rgba(255, 255, 255, 0.3) 0 1px, transparent 1.9px),
		radial-gradient(circle at 66% 92%, rgba(255, 255, 255, 0.28) 0 1.1px, transparent 2px),
		/* crystal spurs feathering off the top edge */
		linear-gradient(200deg, rgba(255, 255, 255, 0.3), transparent 55%),
		linear-gradient(160deg, rgba(255, 255, 255, 0.24), transparent 50%);
	background-size:
		auto, auto, auto,
		auto, auto, auto, auto, auto, auto,
		3px 11px, 3px 8px;
	background-position:
		0 0, 0 0, 0 0,
		0 0, 0 0, 0 0, 0 0, 0 0, 0 0,
		72% 0, 89% 0;
	background-repeat: no-repeat;
	/* Rime grows and gives back a little, on a long cycle — the cabinet
	   cycling its compressor, not a pulsing UI element. */
	animation: ctr-rime-creep 14s ease-in-out infinite;
}
@keyframes ctr-rime-creep {
	0%, 100% { opacity: 0.82; transform: scale(1); }
	50% { opacity: 1; transform: scale(1.015); }
}

/* ── The unit's own light ────────────────────────────────────────────────
   A cold appliance lights the room around it rather than lighting itself
   from inside: a soft bloom anchored on the door, breathing on the same slow
   compressor cycle as the frost and the sweat. It sits below every child
   (z-index -1) so it washes the panel, never the text on it. */
#container-glow {
	display: none;
	position: absolute;
	inset: -12%;
	pointer-events: none;
	z-index: -1;
}
/* Deliberately static. A breathing glow costs a repaint every frame and
   reads as nothing — a fridge's light doesn't throb. The budget went into
   the compartment light bars and the contact shadows under the items. */
#container-box.ctr-theme-consumer #container-glow,
#container-box.ctr-theme-commercial #container-glow {
	display: block;
}
#container-box.ctr-theme-consumer #container-glow {
	background:
		radial-gradient(66% 62% at 76% 52%, color-mix(in srgb, var(--ctr-cold) 52%, transparent), transparent 72%),
		radial-gradient(42% 32% at 70% 18%, color-mix(in srgb, var(--ctr-cold) 30%, transparent), transparent 78%);
}
/* Industrial units throw a dimmer, colder spill with a hazard cast to it. */
#container-box.ctr-theme-commercial #container-glow {
	background:
		radial-gradient(66% 62% at 76% 52%, rgba(198, 210, 218, 0.26), transparent 72%),
		radial-gradient(38% 26% at 72% 20%, color-mix(in srgb, var(--ctr-hazard) 22%, transparent), transparent 78%);
}
/* Brief cold-fog flourish on open — the whole panel reads as fogged-over
   glass that clears, like a fridge door held open a second too long. A
   straight fade, no pulse/brightening, and no center-focused glow. */
#container-box {
	--ctr-fx-tint: 205, 232, 245;
}
#container-box.ctr-theme-commercial {
	--ctr-fx-tint: 198, 210, 218;
}
#container-cold-fx {
	position: absolute;
	inset: 0;
	pointer-events: none;
	opacity: 0;
	background:
		linear-gradient(165deg, rgba(var(--ctr-fx-tint), 0.5), rgba(var(--ctr-fx-tint), 0.32) 55%, rgba(var(--ctr-fx-tint), 0.18));
	z-index: 5;
}
#container-cold-fx.play {
	animation: ctr-cold-clear 1.5s ease-out forwards;
}
@keyframes ctr-cold-clear {
	0% { opacity: 1; transform: translateY(0) scale(1.04); }
	100% { opacity: 0; transform: translateY(14px) scale(1); }
}

/* Every animation here is ambience — it all stops dead when the player has
   motion turned off (or the OS asks for reduced motion). */
[data-motion="off"] .ctr-door-fx i,
[data-motion="off"] .ctr-temp::before,
[data-motion="off"] .ctr-subbox.ctr-frost::before,
[data-motion="off"] .ctr-subbox.ctr-frost::after,
[data-motion="off"] #container-contents-col::before {
	animation: none;
}
[data-motion="off"] .ctr-door-fx i { display: none; }
@media (prefers-reduced-motion: reduce) {
	.ctr-door-fx i,
	.ctr-temp::before,
	.ctr-subbox.ctr-frost::before,
	.ctr-subbox.ctr-frost::after,
	#container-contents-col::before {
		animation: none;
	}
	.ctr-door-fx i { display: none; }
}
#container-header {
	display: flex;
	justify-content: space-between;
	align-items: center;
}
#container-header h2 {
	color: var(--cyan);
	font-size: 14px;
	letter-spacing: 2px;
	text-transform: uppercase;
	margin: 0;
}
#container-close {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	width: 26px;
	height: 26px;
	cursor: pointer;
	border-radius: 2px;
}
#container-close:hover {
	border-color: var(--red);
	color: var(--red);
}
#container-capacity {
	font-family: var(--font-mono, monospace);
	font-size: 11px;
	font-variant-numeric: tabular-nums;
	letter-spacing: 0.6px;
	color: var(--text-dim);
	text-align: center;
}
/* Load gauge under each capacity readout — `--fill` is set from JS as the
   used/total percentage; 0% simply draws an empty track. */
#container-capacity::after,
.ctr-subbox-capacity::after {
	content: "";
	display: block;
	height: 2px;
	margin: 4px auto 0;
	max-width: 220px;
	border-radius: 1px;
	background:
		linear-gradient(90deg, var(--ctr-accent, var(--cyan)) var(--fill, 0%), rgba(255, 255, 255, 0.09) var(--fill, 0%));
	box-shadow: 0 0 6px color-mix(in srgb, var(--ctr-accent, var(--cyan)) 25%, transparent);
	transition: background 0.25s;
}
.ctr-subbox-capacity::after {
	margin-left: 0;
}
#container-notify {
	font-size: 11px;
	color: var(--yellow, #c8a000);
	text-align: center;
	min-height: 14px;
}
#container-body {
	display: flex;
	gap: 0;
	overflow: hidden;
	flex: 1;
	min-height: 0;
}
.container-col {
	flex: 1;
	display: flex;
	flex-direction: column;
	overflow: hidden;
}
#container-inv-col {
	border-right: 1px solid var(--border);
	padding-right: 14px;
}
#container-contents-col {
	padding-left: 14px;
}
/* ── The appliance body — a frameless glass cabinet ──────────────────────
   No hinges, no pull handle: this is a sealed slab of smoked glass in a
   machined trim ring, opened by a capacitive touch rail down the inner edge.
   The shell reuses the vocabulary the TV flatscreen skin settled on — a
   layered directional box-shadow for the bezel, plus a screen-blended
   specular that drifts across the pane — retuned to the appliance's own
   palette. Only when a cold theme is active; crates and bags are untouched. */
#container-box.ctr-theme-consumer #container-contents-col,
#container-box.ctr-theme-commercial #container-contents-col {
	position: relative;
	margin: 5px 6px 5px 10px;
	padding: 14px 14px 14px 24px;
	border-radius: 14px;
	background-repeat: no-repeat;
}
#container-box.ctr-theme-consumer #container-contents-col {
	border: none;
	background-color: rgba(4, 9, 13, 0.35);
	background-image:
		/* ambient glare over the top-left of the pane (static — it belongs to
		   the room's light, and nothing about it needs to move) */
		radial-gradient(120% 85% at 12% 0%, rgba(205, 224, 238, 0.09), transparent 55%),
		/* smoked pane, clearest where the cabinet light hits it */
		linear-gradient(178deg, color-mix(in srgb, var(--ctr-cold) 13%, transparent), transparent 34%, rgba(0, 0, 0, 0.26)),
		/* the unit's own light pooling behind the glass */
		radial-gradient(125% 62% at 50% 0%, color-mix(in srgb, var(--ctr-cold) 20%, transparent), transparent 64%);
	box-shadow:
		/* machined trim ring — top-lit, shadowed underneath, so it reads as a
		   real bevel rather than a drawn outline */
		0 1px 0 1px color-mix(in srgb, var(--ctr-cold) 45%, #ffffff),
		0 0 0 2px color-mix(in srgb, var(--ctr-cold) 45%, #262c31),
		0 2px 0 2px rgba(0, 0, 0, 0.5),
		0 0 0 4px color-mix(in srgb, var(--ctr-cold) 14%, #101418),
		/* the unit standing off the panel */
		0 16px 38px -14px rgba(0, 0, 0, 0.8),
		/* pane seated into the ring */
		inset 0 0 0 1px rgba(255, 255, 255, 0.08),
		inset 0 2px 18px rgba(0, 0, 0, 0.55),
		inset 0 0 60px rgba(0, 0, 0, 0.38);
}
#container-box.ctr-theme-commercial #container-contents-col {
	border: none;
	background-color: rgba(0, 0, 0, 0.32);
	background-image:
		radial-gradient(120% 85% at 12% 0%, rgba(205, 224, 238, 0.08), transparent 55%),
		linear-gradient(178deg, rgba(216, 224, 230, 0.09), transparent 34%, rgba(0, 0, 0, 0.3)),
		radial-gradient(125% 62% at 50% 0%, rgba(216, 224, 230, 0.1), transparent 64%),
		repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.02) 0 1px, transparent 1px 3px);
	box-shadow:
		0 1px 0 1px #aab1b7,
		0 0 0 2px var(--ctr-steel-hi),
		0 2px 0 2px rgba(0, 0, 0, 0.6),
		0 0 0 4px #14171a,
		0 16px 38px -14px rgba(0, 0, 0, 0.85),
		inset 0 0 0 1px rgba(255, 255, 255, 0.06),
		inset 0 2px 18px rgba(0, 0, 0, 0.6),
		inset 0 0 60px rgba(0, 0, 0, 0.45);
}
/* The touch rail — a lit capacitive strip sunk into the inner edge. It does
   the job the pull handle used to do, without any moving hardware. */
#container-box.ctr-theme-consumer #container-contents-col::before,
#container-box.ctr-theme-commercial #container-contents-col::before {
	content: "";
	position: absolute;
	left: 9px;
	top: 20px;
	bottom: 20px;
	width: 3px;
	border-radius: 2px;
	z-index: 3;
	background-repeat: no-repeat;
	background-size: 100% 20%, auto;
	background-position: 0 -30%, 0 0;
	animation: ctr-glint 11s ease-in-out infinite;
}
#container-box.ctr-theme-consumer #container-contents-col::before {
	background-image:
		linear-gradient(180deg, transparent, rgba(255, 255, 255, 0.85) 45% 55%, transparent),
		linear-gradient(180deg,
			color-mix(in srgb, var(--ctr-cold) 25%, transparent),
			var(--ctr-cold) 28% 72%,
			color-mix(in srgb, var(--ctr-cold) 25%, transparent));
	box-shadow:
		0 0 10px color-mix(in srgb, var(--ctr-cold) 60%, transparent),
		inset 0 0 0 1px rgba(0, 0, 0, 0.35);
}
#container-box.ctr-theme-commercial #container-contents-col::before {
	background-image:
		linear-gradient(180deg, transparent, rgba(255, 255, 255, 0.7) 45% 55%, transparent),
		linear-gradient(180deg,
			color-mix(in srgb, var(--ctr-hazard) 20%, transparent),
			var(--ctr-hazard) 28% 72%,
			color-mix(in srgb, var(--ctr-hazard) 20%, transparent));
	box-shadow:
		0 0 9px color-mix(in srgb, var(--ctr-hazard) 45%, transparent),
		inset 0 0 0 1px rgba(0, 0, 0, 0.5);
}
/* The touch rail is absolutely positioned; lift the real
   content above it and the pooled light. */
#container-box.ctr-theme-consumer #container-contents-col > *,
#container-box.ctr-theme-commercial #container-contents-col > *,
.ctr-subbox.ctr-frost > * {
	position: relative;
	z-index: 1;
}

/* ── Behind the glass ────────────────────────────────────────────────────
   Condensation on the inner face of the pane, under the contents (z-index 0
   against their 1) so it never fights the item text. Domestic units only —
   an industrial cabinet runs its pane dry. */
.ctr-door-fx {
	display: none;
	position: absolute;
	inset: 0;
	overflow: hidden;
	pointer-events: none;
	border-radius: 14px;
}
#container-box.ctr-theme-consumer #container-contents-col > .ctr-door-fx {
	display: block;
	z-index: 0;
}
/* The hairline where the slab meets the cabinet — the only seam on the unit,
   with the interior light bleeding through it. */
.ctr-door-fx::before {
	content: "";
	position: absolute;
	top: 10%;
	bottom: 10%;
	right: 9px;
	width: 1px;
	background: linear-gradient(180deg,
		transparent,
		color-mix(in srgb, var(--ctr-cold, #bfe9ff) 60%, transparent) 18% 82%,
		transparent);
	box-shadow: 0 0 6px color-mix(in srgb, var(--ctr-cold, #bfe9ff) 35%, transparent);
}
/* Beads that gather and run, each on its own long cycle so two rarely fall
   together. They track down the gutters, clear of the shelves. */
.ctr-door-fx i {
	position: absolute;
	top: 6%;
	width: 3px;
	height: 3px;
	border-radius: 50%;
	opacity: 0;
	background: radial-gradient(circle at 35% 30%, #ffffff, rgba(200, 238, 255, 0.4));
	box-shadow: 0 0 4px rgba(255, 255, 255, 0.5);
	/* Travels by transform, in viewport units against a panel that is itself
	   sized in vh — same reason as the pane above: animating `top` repaints
	   the layer, and this one sits under the item cards. */
	animation: ctr-drip 13s linear infinite;
}
.ctr-door-fx i:nth-child(1) { right: 4px; animation-duration: 13s; }
.ctr-door-fx i:nth-child(2) { right: 14px; animation-duration: 19s; animation-delay: -7s; }
.ctr-door-fx i:nth-child(3) { left: 16px; animation-duration: 23s; animation-delay: -14s; }
@keyframes ctr-drip {
	0% { transform: translateY(0); opacity: 0; }
	5% { opacity: 0.9; }
	8% { transform: translateY(1vh); }   /* hangs a moment before it lets go */
	60% { opacity: 0.75; }
	72% { transform: translateY(56vh); opacity: 0; }
	100% { transform: translateY(56vh); opacity: 0; }
}

/* Compartment header row: label on the left, temperature readout right. */
.ctr-shelf-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	flex-shrink: 0;
	margin-bottom: 8px;
}
.ctr-shelf-head .container-section-label,
.ctr-subbox .ctr-shelf-head .container-section-label {
	margin-bottom: 0;
}
/* Small LED-style temperature readout. Empty (and hidden) unless the panel
   is themed cold and the compartment reports a preservation tier. */
.ctr-temp {
	display: none;
	align-items: baseline;
	gap: 6px;
	font-family: var(--font-mono, monospace);
	line-height: 1;
	padding: 5px 9px 5px 8px;
	border-radius: 4px;
	font-variant-numeric: tabular-nums;
	background: linear-gradient(180deg, rgba(0, 0, 0, 0.62), rgba(0, 0, 0, 0.48));
	border: 1px solid rgba(255, 255, 255, 0.1);
	box-shadow:
		inset 0 1px 3px rgba(0, 0, 0, 0.75),
		inset 0 0 12px color-mix(in srgb, currentColor 16%, transparent),
		0 1px 0 rgba(255, 255, 255, 0.05);
	flex-shrink: 0;
}
.ctr-temp.active {
	display: inline-flex;
}
/* The setpoint name, set small and wide against the large numerals — the
   contrast between the two is what makes a readout look engineered rather
   than merely small. */
.ctr-temp-mode {
	font-size: 8px;
	letter-spacing: 1.6px;
	text-transform: uppercase;
	opacity: 0.62;
	align-self: center;
	padding-right: 7px;
	border-right: 1px solid color-mix(in srgb, currentColor 22%, transparent);
}
.ctr-temp-val {
	font-size: 15px;
	font-weight: 700;
	letter-spacing: 0.5px;
}
.ctr-temp-unit {
	font-size: 9px;
	letter-spacing: 0.5px;
	opacity: 0.8;
}
/* Compressor-status pip, breathing slowly beside the reading. */
.ctr-temp::before {
	content: "";
	width: 5px;
	height: 5px;
	flex-shrink: 0;
	align-self: center;
	border-radius: 50%;
	background: currentColor;
	box-shadow: 0 0 5px currentColor;
	animation: ctr-temp-pip 3.2s ease-in-out infinite;
}
@keyframes ctr-temp-pip {
	0%, 100% { opacity: 0.85; }
	50% { opacity: 0.25; }
}
#container-box.ctr-theme-consumer .ctr-temp {
	color: #bfe9ff;
	text-shadow: 0 0 6px rgba(159, 216, 255, 0.75);
	border-color: rgba(159, 216, 255, 0.3);
}
#container-box.ctr-theme-commercial .ctr-temp {
	color: var(--ctr-hazard);
	text-shadow: 0 0 6px rgba(232, 184, 75, 0.6);
	border-color: rgba(232, 184, 75, 0.28);
}
.ctr-temp.ctr-temp-frozen {
	color: color-mix(in srgb, var(--ctr-frost, #cfeeff) 70%, #ffffff) !important;
	text-shadow: 0 0 7px color-mix(in srgb, var(--ctr-frost, #cfeeff) 85%, transparent) !important;
	border-color: color-mix(in srgb, var(--ctr-frost, #cfeeff) 38%, transparent) !important;
}

/* ── Compartment interiors ───────────────────────────────────────────────
   A lit well: interior light washing down from the top, darkness pooling at
   the bottom. The shelves themselves are drawn per item card (below) rather
   than at a fixed pitch here — a repeating background can't know where the
   variable-height cards are, so its lines sliced through them. */
#container-box.ctr-theme-consumer #container-contents-list,
#container-box.ctr-theme-commercial #container-contents-list,
#container-box.ctr-theme-consumer #container-freezer-list,
#container-box.ctr-theme-commercial #container-freezer-list {
	position: relative;
	border-radius: 4px;
	padding: 12px 6px 10px;
	background-repeat: no-repeat;
	scrollbar-width: thin;
	scrollbar-color: color-mix(in srgb, var(--ctr-accent) 45%, transparent) transparent;
}
#container-box.ctr-theme-consumer #container-contents-list,
#container-box.ctr-theme-consumer #container-freezer-list {
	background-image:
		linear-gradient(180deg, color-mix(in srgb, var(--ctr-cold) 9%, transparent), transparent 40%),
		radial-gradient(120% 40% at 50% 100%, rgba(0, 0, 0, 0.3), transparent 70%);
	box-shadow: inset 0 0 22px rgba(0, 0, 0, 0.3);
}
#container-box.ctr-theme-commercial #container-contents-list,
#container-box.ctr-theme-commercial #container-freezer-list {
	background-image:
		linear-gradient(180deg, rgba(255, 255, 255, 0.05), transparent 38%),
		radial-gradient(120% 40% at 50% 100%, rgba(0, 0, 0, 0.4), transparent 70%);
	box-shadow: inset 0 0 22px rgba(0, 0, 0, 0.42);
}
/* The light bar — actual hardware at the top of each compartment, blazing at
   its centre and falling off to nothing at the ends, throwing a wide soft
   spill down into the well. This is where the light in here comes FROM;
   before, the wells were lit by a gradient with no source. */
#container-box.ctr-theme-consumer #container-contents-list::before,
#container-box.ctr-theme-consumer #container-freezer-list::before,
#container-box.ctr-theme-commercial #container-contents-list::before,
#container-box.ctr-theme-commercial #container-freezer-list::before {
	content: "";
	position: absolute;
	top: 4px;
	left: 12%;
	right: 12%;
	height: 3px;
	border-radius: 2px;
	pointer-events: none;
}
#container-box.ctr-theme-consumer #container-contents-list::before,
#container-box.ctr-theme-consumer #container-freezer-list::before {
	background: linear-gradient(90deg,
		transparent,
		color-mix(in srgb, var(--ctr-cold) 70%, transparent) 14%,
		#ffffff 50%,
		color-mix(in srgb, var(--ctr-cold) 70%, transparent) 86%,
		transparent);
	box-shadow:
		0 0 12px color-mix(in srgb, var(--ctr-cold) 70%, transparent),
		0 10px 34px -6px color-mix(in srgb, var(--ctr-cold) 40%, transparent);
}
#container-box.ctr-theme-commercial #container-contents-list::before,
#container-box.ctr-theme-commercial #container-freezer-list::before {
	background: linear-gradient(90deg, transparent, rgba(226, 235, 244, 0.65) 14%, #ffffff 50%, rgba(226, 235, 244, 0.65) 86%, transparent);
	box-shadow:
		0 0 12px rgba(226, 235, 244, 0.5),
		0 10px 34px -6px rgba(226, 235, 244, 0.22);
}

/* An empty compartment has no cards to hang shelves off, and a bare well that
   tall reads as a bug. Draw the empty shelf run instead — nothing is standing
   on them, so a fixed pitch is exactly right here. */
#container-box.ctr-theme-consumer #container-contents-list:empty,
#container-box.ctr-theme-consumer #container-freezer-list:empty,
#container-box.ctr-theme-commercial #container-contents-list:empty,
#container-box.ctr-theme-commercial #container-freezer-list:empty {
	background-repeat: no-repeat, no-repeat, repeat-y;
	background-size: 100% 100%, 100% 100%, 100% 62px;
	background-position: 0 0, 0 0, 0 24px;
}
#container-box.ctr-theme-consumer #container-contents-list:empty,
#container-box.ctr-theme-consumer #container-freezer-list:empty {
	background-image:
		linear-gradient(180deg, color-mix(in srgb, var(--ctr-cold) 9%, transparent), transparent 40%),
		radial-gradient(120% 40% at 50% 100%, rgba(0, 0, 0, 0.3), transparent 70%),
		linear-gradient(180deg,
			transparent 0 58px,
			var(--ctr-shelf-lip) 58px 59px,
			rgba(0, 0, 0, 0.5) 59px 61px,
			transparent 61px 62px);
}
#container-box.ctr-theme-commercial #container-contents-list:empty,
#container-box.ctr-theme-commercial #container-freezer-list:empty {
	background-image:
		linear-gradient(180deg, rgba(255, 255, 255, 0.05), transparent 38%),
		radial-gradient(120% 40% at 50% 100%, rgba(0, 0, 0, 0.4), transparent 70%),
		linear-gradient(180deg,
			transparent 0 58px,
			var(--ctr-shelf-lip) 58px 59px,
			rgba(0, 0, 0, 0.5) 59px 61px,
			transparent 61px 62px);
}

/* Every card stands on its own shelf lip — a lit edge over its shadow, drawn
   in the gutter under the card so shelf and contents always line up. */
/* Only the cards actually inside the appliance are chilled — the inventory
   column is your pockets, and keeps the standard card look. */
#container-box.ctr-theme-consumer #container-contents-list .ctr-item-card,
#container-box.ctr-theme-consumer #container-freezer-list .ctr-item-card,
#container-box.ctr-theme-commercial #container-contents-list .ctr-item-card,
#container-box.ctr-theme-commercial #container-freezer-list .ctr-item-card {
	position: relative;
	background: var(--ctr-card-bg);
	border-color: var(--ctr-card-border);
	/* A contact shadow, so the item is standing ON the shelf rather than
	   floating in front of it. Cheap, static, and does more for the sense of
	   depth in here than any amount of animation. */
	box-shadow:
		0 6px 10px -6px rgba(0, 0, 0, 0.8),
		inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
/* Light bouncing off the shelf beneath the item — a soft smear the width of
   the card, tucked under it and above the shelf lip. */
#container-box.ctr-theme-consumer #container-contents-list .ctr-item-card::before,
#container-box.ctr-theme-consumer #container-freezer-list .ctr-item-card::before,
#container-box.ctr-theme-commercial #container-contents-list .ctr-item-card::before,
#container-box.ctr-theme-commercial #container-freezer-list .ctr-item-card::before {
	content: "";
	position: absolute;
	left: 10%;
	right: 10%;
	bottom: -6px;
	height: 6px;
	border-radius: 50%;
	pointer-events: none;
	filter: blur(2px);
	background: radial-gradient(ellipse at 50% 0%, var(--ctr-shelf-lip), transparent 72%);
	opacity: 0.5;
}
#container-box.ctr-theme-consumer #container-contents-list .ctr-item-card::after,
#container-box.ctr-theme-consumer #container-freezer-list .ctr-item-card::after,
#container-box.ctr-theme-commercial #container-contents-list .ctr-item-card::after,
#container-box.ctr-theme-commercial #container-freezer-list .ctr-item-card::after {
	content: "";
	position: absolute;
	left: -6px;
	right: -6px;
	bottom: -6px;
	height: 3px;
	border-radius: 1px;
	pointer-events: none;
	background: linear-gradient(180deg, var(--ctr-shelf-lip) 0 1px, rgba(0, 0, 0, 0.55) 1px);
}
#container-box.ctr-theme-consumer .ctr-item-card:hover,
#container-box.ctr-theme-commercial .ctr-item-card:hover,
#container-box.ctr-theme-consumer #container-contents-list .ctr-item-card:hover,
#container-box.ctr-theme-consumer #container-freezer-list .ctr-item-card:hover,
#container-box.ctr-theme-commercial #container-contents-list .ctr-item-card:hover,
#container-box.ctr-theme-commercial #container-freezer-list .ctr-item-card:hover {
	border-color: var(--ctr-accent);
	box-shadow:
		0 6px 10px -6px rgba(0, 0, 0, 0.8),
		inset 0 1px 0 rgba(255, 255, 255, 0.06),
		0 0 12px color-mix(in srgb, var(--ctr-accent) 25%, transparent);
}
#container-box.ctr-theme-consumer .ctr-action-btn:hover,
#container-box.ctr-theme-commercial .ctr-action-btn:hover {
	border-color: var(--ctr-accent);
	color: var(--ctr-accent);
}
#container-box.ctr-theme-consumer .ctr-drag-over,
#container-box.ctr-theme-commercial .ctr-drag-over {
	border-color: var(--ctr-accent);
	background-color: color-mix(in srgb, var(--ctr-accent) 8%, transparent);
}
/* The "(empty)" line reads as a label inside a lit, empty interior. */
#container-box.ctr-theme-consumer #container-contents-list:empty::after,
#container-box.ctr-theme-consumer #container-freezer-list:empty::after,
#container-box.ctr-theme-commercial #container-contents-list:empty::after,
#container-box.ctr-theme-commercial #container-freezer-list:empty::after {
	margin: auto;
	font-family: var(--font-mono, monospace);
	font-style: normal;
	font-size: 10px;
	letter-spacing: 3.5px;
	text-indent: 3.5px;                  /* balance the trailing letter-space */
	text-transform: uppercase;
	opacity: 0.5;
}
/* Freezer sub-box: shown only when the opened container has a paired box
   (e.g. a fridge's separate freezer compartment) — a smaller drop zone
   stacked above the main contents list, same panel, no second `open`. */
.ctr-subbox {
	display: none;
	flex-direction: column;
	flex-shrink: 0;
	max-height: 35%;
	border: 1px solid var(--border);
	border-radius: 4px;
	padding: 8px;
	margin-bottom: 10px;
	background: rgba(255, 255, 255, 0.02);
}
.ctr-subbox.active {
	display: flex;
}
.ctr-subbox .container-section-label {
	margin-bottom: 2px;
}
.ctr-subbox-capacity {
	font-family: var(--font-mono, monospace);
	font-size: 10px;
	font-variant-numeric: tabular-nums;
	letter-spacing: 0.5px;
	color: var(--text-dim);
	margin-bottom: 6px;
}
#container-freezer-list {
	flex: 1;
	overflow-y: auto;
	min-height: 40px;
	display: flex;
	flex-direction: column;
	gap: 6px;
	padding: 4px;
	border: 1px dashed transparent;
	border-radius: 3px;
	transition: border-color 0.15s, background 0.15s;
}
#container-freezer-list.ctr-drag-over {
	border-color: var(--cyan);
	background: rgba(40, 229, 255, 0.05);
}
#container-freezer-list:empty::after {
	content: "(empty)";
	color: var(--text-dim);
	font-style: italic;
	font-size: 11px;
	padding: 4px;
}
#container-divider {
	flex-shrink: 0;
	display: flex;
	align-items: center;
	font-size: 18px;
	color: var(--text-dim);
	padding: 0 4px;
}
.container-section-label {
	font-size: 10px;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 2.2px;
	color: var(--text-dim);
	margin-bottom: 8px;
	flex-shrink: 0;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
#container-inv-list,
#container-contents-list {
	flex: 1;
	overflow-y: auto;
	min-height: 80px;
	display: flex;
	flex-direction: column;
	gap: 6px;
	padding: 4px;
	border: 1px dashed transparent;
	border-radius: 3px;
	transition:
		border-color 0.15s,
		background 0.15s;
}
#container-inv-list.ctr-drag-over,
#container-contents-list.ctr-drag-over {
	border-color: var(--cyan);
	background: rgba(40, 229, 255, 0.05);
}
#container-inv-list:empty::after,
#container-contents-list:empty::after {
	content: "(empty)";
	color: var(--text-dim);
	font-style: italic;
	font-size: 11px;
	padding: 4px;
}
.ctr-item-card {
	background: var(--bg3);
	border: 1px solid var(--border);
	border-radius: 4px;
	padding: 9px 10px;
	min-height: 38px;
	cursor: grab;
	transition: border-color 0.15s, box-shadow 0.15s, transform 0.12s;
	display: flex;
	align-items: center;
	gap: 8px;
}
.ctr-item-card:hover {
	border-color: var(--cyan);
}
.ctr-item-card.dragging {
	opacity: 0.4;
}
/* Type hierarchy on a card: the name carries it at a readable size, the
   quantity is a badge, and the weight is small tabular mono hard against the
   right edge so a column of cards lines up as a table would. */
.ctr-name {
	color: var(--text-bright);
	flex: 1;
	min-width: 0;
	font-size: 12.5px;
	line-height: 1.3;
	letter-spacing: 0.2px;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.ctr-qty {
	flex-shrink: 0;
	font-family: var(--font-mono, monospace);
	font-size: 10px;
	font-weight: 700;
	line-height: 1;
	padding: 3px 5px;
	border-radius: 3px;
	color: var(--text);
	background: rgba(255, 255, 255, 0.07);
	box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.07);
}
.ctr-meta {
	flex-shrink: 0;
	color: var(--text-dim);
	font-family: var(--font-mono, monospace);
	font-size: 10.5px;
	font-variant-numeric: tabular-nums;
	letter-spacing: 0.3px;
	min-width: 46px;
	text-align: right;
}
.ctr-action-btn {
	display: none;
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-size: 9px;
	letter-spacing: 1.4px;
	text-transform: uppercase;
	padding: 4px 8px;
	cursor: pointer;
	border-radius: 3px;
	flex-shrink: 0;
}
.ctr-action-btn:hover {
	border-color: var(--cyan);
	color: var(--cyan);
}
/* The button holds its space and fades in, rather than appearing and shoving
   the name and weight sideways on every hover. */
.ctr-item-card .ctr-action-btn {
	display: block;
	opacity: 0;
	pointer-events: none;
	transform: translateX(3px);
	transition: opacity 0.15s, transform 0.15s, border-color 0.15s, color 0.15s;
}
.ctr-item-card:hover .ctr-action-btn,
.ctr-action-btn:focus-visible {
	opacity: 1;
	pointer-events: auto;
	transform: none;
}

/* Quantity split dialog */
/* Shared backdrop for JS-created modals — invariant layout only. Each overlay keeps its
   own display / background-opacity / z-index inline, since those vary per modal. */
.modal-overlay {
	position: fixed;
	inset: 0;
	align-items: center;
	justify-content: center;
}

/* Inline radio-option label used across the auth/creation form. */
.radio-label {
	display: flex;
	align-items: center;
	gap: 6px;
	cursor: pointer;
}

/* Light View panel buttons. Accent colour is passed per-button via --lv-btn-color. */
.lv-btn {
	background: transparent;
	border: 1px solid var(--lv-btn-color);
	color: var(--lv-btn-color);
	font-family: var(--font-mono);
	font-size: 10px;
	padding: 3px 8px;
	cursor: pointer;
	border-radius: 2px;
	margin-left: 4px;
}
.qty-dialog-overlay {
	position: fixed;
	inset: 0;
	background: transparent;
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 9999;
}
.qty-dialog {
	background: var(--bg2, #1a1a1a);
	border: 1px solid var(--border, #333);
	padding: 18px 22px;
	min-width: 200px;
	display: flex;
	flex-direction: column;
	gap: 10px;
}
.qty-dialog-label {
	color: var(--fg, #ccc);
	font-size: 0.9em;
}
.qty-dialog-input {
	background: var(--bg, #111);
	border: 1px solid var(--border, #444);
	color: var(--fg, #ccc);
	padding: 4px 8px;
	font-size: 1em;
	width: 100%;
	box-sizing: border-box;
}
.qty-dialog-btns {
	display: flex;
	gap: 8px;
}
.qty-dialog-ok,
.qty-dialog-cancel {
	flex: 1;
	padding: 5px 0;
	background: none;
	border: 1px solid var(--border, #444);
	color: var(--fg, #ccc);
	cursor: pointer;
	font-size: 0.85em;
}
.qty-dialog-ok:hover { border-color: var(--cyan, #0ff); color: var(--cyan, #0ff); }
.qty-dialog-cancel:hover { border-color: var(--red, #f44); color: var(--red, #f44); }

@media (max-width: 600px) {
	#container-body {
		flex-direction: column;
		overflow-y: auto;
	}
	#container-inv-col {
		border-right: none;
		padding-right: 0;
		border-bottom: 1px solid var(--border);
		padding-bottom: 12px;
		margin-bottom: 4px;
	}
	#container-contents-col {
		padding-left: 0;
	}
	#container-divider {
		display: none;
	}
}

#map-close {
	width: 100%;
	background: linear-gradient(165deg, var(--map-surface-hi), var(--map-surface-lo));
	border: 1px solid color-mix(in srgb, var(--accent) 32%, transparent);
	color: var(--accent);
	font-family: var(--font-mono);
	font-size: 12px;
	padding: 9px;
	cursor: pointer;
	letter-spacing: 1px;
	text-transform: uppercase;
	border-radius: 6px;
	box-shadow: inset 0 1px 0 var(--map-bevel-hi), inset 0 -2px 3px var(--map-bevel-lo), 0 2px 4px rgba(0, 0, 0, 0.22);
	transition: filter 0.12s, box-shadow 0.12s, transform 0.05s;
}
#map-close:hover {
	filter: brightness(1.2);
}
#map-close:active {
	transform: translateY(1px);
	box-shadow: inset 0 1px 3px var(--map-bevel-lo);
}

/* Dialogue panel */
#dialogue-panel {
	display: none;
	position: fixed;
	inset: 0;
	background: transparent;
	z-index: 100;
	/* Anchored low and wide rather than dead-centre: a centred rectangle reads as
	   an OS dialog no matter how big it gets, while a low band leaves the room
	   above it visible and reads as the world being talked over. */
	align-items: flex-end;
	justify-content: center;
	padding-bottom: 4vh;
	box-sizing: border-box;
}
#dialogue-panel.active {
	display: flex;
}

/* The panel takes the floor: rises into place rather than popping. Exit is the
   same move reversed, driven by `.closing` (see closeDialogue in dialogue.js). */
@keyframes dlg-in {
	from { opacity: 0; transform: translateY(22px) scale(0.985); }
	to   { opacity: 1; transform: none; }
}
@keyframes dlg-out {
	from { opacity: 1; transform: none; }
	to   { opacity: 0; transform: translateY(14px) scale(0.99); }
}
#dialogue-panel.active #dialogue-box {
	animation: dlg-in 0.18s ease-out;
}
#dialogue-panel.closing #dialogue-box {
	animation: dlg-out 0.15s ease-in forwards;
}

#dialogue-box {
	background: var(--bg2);
	border: 1px solid var(--accent);
	padding: 22px 26px;
	width: 760px;
	max-width: 92vw;
	min-height: 420px;
	max-height: 78vh;
	border-radius: 2px;
	display: flex;
	flex-direction: column;
	box-sizing: border-box;
}
#dialogue-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	margin-bottom: 6px;
	flex-shrink: 0;
}
#dialogue-npc-name {
	color: var(--accent);
	font-weight: bold;
	font-size: 21px;
	text-transform: uppercase;
	letter-spacing: 2px;
}
#dialogue-close {
	background: none;
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 14px;
	line-height: 1;
	padding: 1px 6px;
	cursor: pointer;
	border-radius: 2px;
	flex-shrink: 0;
}
#dialogue-close:hover {
	border-color: var(--accent);
	color: var(--accent);
}
#dialogue-text {
	color: var(--text-bright);
	margin-bottom: 14px;
	font-size: 22px;
	line-height: 1.5;
	white-space: pre-wrap;
	overflow-y: auto;
	flex: 1;
	min-height: 160px;
}
/* Shop mode reuses #dialogue-text as its two-pane container — keep that dense. */
#dialogue-box.shop-mode #dialogue-text { font-size: 13px; }
/* The speaker still talking. Sits in place of the line while the NPC takes their
   beat (THINK_MS in dialogue.js) so the panel visibly waits on them. */
.dialogue-thinking {
	color: var(--text-dim);
	letter-spacing: 4px;
	animation: dlg-think 0.9s ease-in-out infinite;
}
@keyframes dlg-think {
	0%, 100% { opacity: 0.25; }
	50%      { opacity: 0.8; }
}

/* Stage direction — what the speaker's body is doing, and what changed in their
   manner when a line of yours moved reputation. Server-authored (`stage` on the
   dialogue message); italic and dim so it never reads as speech. */
#dialogue-stage {
	display: none;
	font-style: italic;
	color: var(--text-dim);
	font-size: 16px;
	line-height: 1.4;
	margin-bottom: 10px;
	flex-shrink: 0;
}
#dialogue-stage.on {
	display: block;
}

/* Stakes band — one line saying where the hovered/focused option goes. Holds its
   height always so the option column never shifts as the pointer moves. */
#dialogue-hint {
	min-height: 1.5em;
	font-size: 14px;
	line-height: 1.5;
	color: var(--cyan);
	opacity: 0.8;
	margin-bottom: 6px;
	flex-shrink: 0;
}
/* The stakes of an irreversible option are stated in its own colour. */
#dialogue-hint.danger {
	color: var(--red);
	opacity: 1;
}

#dialogue-options {
	display: flex;
	flex-direction: column;
	gap: 7px;
	flex-shrink: 0;
}
/* Veiled = built but not yet offered: the options (and the stage line) hold their
   space while the speaker is still talking, so nothing reflows when they land. */
#dialogue-options.veiled,
#dialogue-stage.veiled {
	opacity: 0;
	pointer-events: none;
}
#dialogue-options,
#dialogue-stage {
	transition: opacity 0.22s ease;
}

/* Shop mode is a dense terminal — the conversation bands don't belong in it. */
#dialogue-box.shop-mode #dialogue-stage,
#dialogue-box.shop-mode #dialogue-hint {
	display: none;
}

/* Speaker mood, from your standing with the org they belong to (`mood` on the
   dialogue message). Text-only: it tints and names the plate, nothing else. */
#dialogue-box[data-mood='hostile'] #dialogue-npc-name { color: var(--red); }
#dialogue-box[data-mood='cold'] #dialogue-npc-name { color: var(--text-dim); }
#dialogue-box[data-mood='warm'] #dialogue-npc-name { color: var(--green); }
#dialogue-box[data-mood='devoted'] #dialogue-npc-name { color: var(--purple); }
#dialogue-box[data-mood] #dialogue-npc-name::after {
	font-size: 13px;
	font-weight: normal;
	letter-spacing: 1px;
	opacity: 0.65;
	margin-left: 10px;
}
#dialogue-box[data-mood='hostile'] #dialogue-npc-name::after { content: '· will not forget you'; }
#dialogue-box[data-mood='cold'] #dialogue-npc-name::after { content: '· does not know you'; }
#dialogue-box[data-mood='warm'] #dialogue-npc-name::after { content: '· knows your face'; }
#dialogue-box[data-mood='devoted'] #dialogue-npc-name::after { content: '· one of theirs'; }
.dialogue-opt {
	background: var(--bg3);
	border: 1px solid var(--border);
	color: var(--text);
	font-family: var(--font-mono);
	font-size: 13px;
	padding: 5px 10px;
	cursor: pointer;
	text-align: left;
	border-radius: 2px;
	transition: all 0.1s;
}
#dialogue-box:not(.shop-mode) .dialogue-opt {
	font-size: 18px;
	padding: 10px 14px;
}
.dialogue-opt:hover {
	border-color: var(--accent);
	color: var(--accent);
}
/* An option you can't walk back — starts violence, gets you charged, burns
   standing (server tags it `_kind: 'hostile'`). Red edging plus its own stakes
   line in #dialogue-hint; it must not look like every other choice. */
.dialogue-opt-hostile {
	border-color: var(--red);
	color: var(--red);
	box-shadow: inset 2px 0 0 var(--red);
}
.dialogue-opt-hostile:hover {
	border-color: var(--red);
	color: #fff;
	background: rgba(255, 59, 92, 0.14);
}

.dialogue-opt-branch {
	color: var(--accent);
	margin-right: 4px;
	font-style: normal;
}
/* Destination glyph on an option that opens a shop / takes or hands in a job /
   ends the conversation. Server tags the option (`_kind`); the icon sits ahead of
   the label so the column of options reads down its left edge. */
.dialogue-opt-icon {
	margin-right: 5px;
	font-size: 12px;
	opacity: 0.85;
}
#dialogue-box:not(.shop-mode) .dialogue-opt-icon {
	margin-right: 8px;
	font-size: 16px;
}

/* A turn-in option for a quest that's accepted but not yet finished: looks
   dimmed/locked, but is still tappable — it routes to the Tablet quest screen. */
.dialogue-opt-locked {
	opacity: 0.5;
	font-style: italic;
}
.dialogue-opt-locked::before {
	content: "\1F512"; /* padlock */
	margin-right: 5px;
	font-style: normal;
}
.dialogue-opt-locked:hover {
	border-color: var(--border);
	color: var(--text-dim, var(--text));
}

/* ─── Shop (GUI vendor) — two-pane "Terminal Card" layout ──────────────────
   List of items on the left, an examine card on the right, a transaction footer
   below. Rendered into #dialogue-text by renderShop() in dialogue.js. The box is
   widened to a fixed size via the .shop-mode class while a shop is open. */
#dialogue-box.shop-mode {
	width: 780px;
	max-width: 94vw;
	height: 560px;
	max-height: 84vh;
}
#dialogue-box.shop-mode #dialogue-text {
	overflow: hidden;
	white-space: normal;
	min-height: 0;
	margin-bottom: 8px;
}
/* Back + Leave share one row; the panel's static Leave button (a direct child
   of the box) is hidden so only the in-options pair shows. */
#dialogue-box.shop-mode #dialogue-options { flex-direction: row; gap: 6px; }
#dialogue-box.shop-mode #dialogue-options .shop-back { flex: 1; }
#dialogue-box.shop-mode > .dialogue-opt { display: none; }

/* Reactive purchase feedback lives on the credits counter — it rolls to the new
   balance (animateCredits in dialogue.js), tinted red while spending and green
   while gaining, then eases back to its resting colour. */
.shop-cred b { transition: color 0.25s ease; }
.shop-cred.cred-up b { color: var(--green); }
.shop-cred.cred-down b { color: var(--red); }

.shop2 {
	height: 100%;
	display: flex;
	flex-direction: column;
	white-space: normal;
	font-size: 12px;
	background: repeating-linear-gradient(0deg, transparent 0 3px, rgba(255, 46, 196, 0.02) 3px 4px);
}
.shop-bar {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding-bottom: 8px;
	border-bottom: 1px dashed var(--accent-dim);
	flex-shrink: 0;
}
.shop-modes { display: flex; align-items: center; gap: 16px; }
.shop-modes > button {
	background: none;
	border: none;
	color: var(--text-dim);
	cursor: pointer;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	padding: 2px 0;
	font-family: inherit;
	font-size: 12px;
}
.shop-modes > button.on { color: var(--cyan); }
.shop-modes > button.on::before { content: "["; color: var(--cyan); }
.shop-modes > button.on::after { content: "]"; color: var(--cyan); }
.shop-sort {
	display: flex;
	align-items: center;
	gap: 6px;
	font-size: 12px;
	color: var(--text-dim);
	letter-spacing: 0.12em;
	text-transform: uppercase;
}
.shop-sort button {
	background: var(--bg3);
	border: 1px solid var(--border);
	color: var(--text-dim);
	border-radius: 3px;
	padding: 2px 8px;
	cursor: pointer;
	font-size: 12px;
	text-transform: uppercase;
	letter-spacing: 0.12em;
	font-family: inherit;
}
.shop-sort button.on { color: #04121a; background: var(--cyan); border-color: var(--cyan); }
.shop-bar-right { display: flex; align-items: center; gap: 16px; }
.shop-cred { font-size: 12px; color: var(--text-dim); white-space: nowrap; }
.shop-cred b { color: var(--yellow); font-size: 15px; font-variant-numeric: tabular-nums; }
.shop-result {
	padding-top: 6px;
	/* Always holds its spot so the panes don't jump when a quip appears. */
	min-height: 2.6em;
	/* The buy/sell line + the vendor's reaction are joined by \n; honour it. */
	white-space: pre-line;
	font-size: 11px;
	color: var(--text-dim);
	flex-shrink: 0;
}
.shop-2pane {
	display: grid;
	grid-template-columns: 1fr 300px;
	flex: 1;
	min-height: 0;
	margin-top: 8px;
}
.shop-list {
	overflow-y: auto;
	min-height: 0;
	border-right: 1px dashed var(--accent-dim);
	padding-right: 2px;
}
/* Themed scrollbars for the two scrolling regions (list + description). */
.shop-list, .shop-card-desc { scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
.shop-list::-webkit-scrollbar, .shop-card-desc::-webkit-scrollbar { width: 8px; }
.shop-list::-webkit-scrollbar-track, .shop-card-desc::-webkit-scrollbar-track { background: transparent; }
.shop-list::-webkit-scrollbar-thumb, .shop-card-desc::-webkit-scrollbar-thumb {
	background: var(--border);
	border-radius: 4px;
}
.shop-list::-webkit-scrollbar-thumb:hover, .shop-card-desc::-webkit-scrollbar-thumb:hover {
	background: var(--accent-dim);
}
.shop-row {
	display: flex;
	justify-content: space-between;
	gap: 10px;
	padding: 7px 10px;
	cursor: pointer;
	border: 1px solid transparent;
}
.shop-row:hover { background: rgba(40, 229, 255, 0.05); }
.shop-row.sel { border-color: var(--cyan); background: rgba(40, 229, 255, 0.07); }
.shop-row .nm { font-size: 13px; }
.shop-row .nm::before { content: "· "; color: var(--text-dim); }
.shop-row.sel .nm::before { content: "› "; color: var(--cyan); }
.shop-row .wg { color: var(--text-dim); opacity: 0.7; }
.shop-row .pr {
	color: var(--text-dim);
	font-size: 12px;
	white-space: nowrap;
	font-variant-numeric: tabular-nums;
}
.shop-row .pr.noafford { color: var(--red); }
.shop-empty { color: var(--text-dim); text-align: center; padding: 20px; font-size: 12px; }
.shop-card { padding-left: 14px; min-height: 0; display: flex; }
.shop-card-empty {
	color: var(--text-dim);
	font-size: 12px;
	text-align: center;
	margin: auto;
	padding: 20px;
}
.shop-card-inner {
	border: 1px solid var(--accent-dim);
	border-radius: 2px;
	padding: 14px;
	flex: 1;
	display: flex;
	flex-direction: column;
	min-height: 0;
	background: var(--bg3);
}
.shop-card-name { color: var(--text-bright); font-size: 15px; }
.shop-card-cat {
	color: var(--purple);
	font-size: 10px;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	margin-top: 4px;
}
.shop-card-inner hr {
	border: none;
	border-top: 1px dashed var(--accent-dim);
	margin: 12px 0;
	width: 100%;
}
.shop-card-desc {
	color: var(--text-dim);
	font-size: 12px;
	line-height: 1.6;
	flex: 1;
	overflow-y: auto;
	min-height: 24px;
}
.shop-stats { margin-top: 10px; display: flex; flex-direction: column; gap: 2px; }
.shop-statline {
	display: flex;
	justify-content: space-between;
	font-size: 11px;
	color: var(--text-dim);
}
.shop-statline b { font-weight: 700; color: var(--text); }
.shop-statline b.stat-dmg { color: var(--orange); }
.shop-statline b.stat-soak { color: var(--cyan); }
.shop-statline b.stat-good { color: var(--green); }
.shop-card-meta {
	font-size: 11px;
	color: var(--text-dim);
	line-height: 1.9;
	margin-top: 10px;
	display: grid;
	grid-template-columns: 1fr auto;
	flex-shrink: 0;
}
.shop-card-meta b { color: var(--text); text-align: right; font-variant-numeric: tabular-nums; }
.shop-qtywrap { display: flex; align-items: center; gap: 8px; margin-top: 12px; flex-shrink: 0; }
.shop-qty {
	display: flex;
	align-items: center;
	border: 1px solid var(--accent-dim);
	border-radius: 3px;
	overflow: hidden;
}
.shop-qty button {
	background: var(--bg3);
	border: none;
	color: var(--cyan);
	width: 30px;
	height: 30px;
	font-size: 16px;
	cursor: pointer;
	font-family: inherit;
}
.shop-qty button:disabled { color: var(--border); cursor: not-allowed; }
.shop-qty span {
	min-width: 44px;
	text-align: center;
	font-size: 14px;
	font-variant-numeric: tabular-nums;
}
.shop-max {
	background: var(--bg3);
	border: 1px solid var(--accent-dim);
	color: var(--cyan);
	border-radius: 3px;
	height: 30px;
	padding: 0 12px;
	font-size: 11px;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	cursor: pointer;
	font-family: inherit;
}
.shop-max:hover { background: var(--accent-dim); color: var(--text-bright); }
.shop-max:disabled { color: var(--border); border-color: var(--border); cursor: not-allowed; }
.shop-foot {
	display: flex;
	gap: 10px;
	align-items: center;
	padding-top: 10px;
	margin-top: 8px;
	border-top: 1px dashed var(--accent-dim);
	flex-shrink: 0;
}
.shop-foot:empty { display: none; }
.shop-total { flex: 1; font-size: 12px; color: var(--text-dim); }
.shop-total b { color: var(--yellow); font-size: 15px; }
.shop-exec {
	background: var(--cyan);
	color: #04121a;
	border: none;
	border-radius: 3px;
	padding: 11px 22px;
	font-size: 13px;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	font-weight: 700;
	cursor: pointer;
	font-family: inherit;
	transition: transform 0.06s ease, box-shadow 0.15s ease;
}
.shop-exec:hover { box-shadow: 0 0 16px -3px var(--cyan); }
.shop-exec:active:not(:disabled) { transform: translateY(1px) scale(0.97); box-shadow: 0 0 20px -2px var(--cyan); }
.shop-exec:disabled { background: var(--bg3); color: var(--red); cursor: not-allowed; box-shadow: none; }
.shop-sellall {
	flex: 1;
	background: var(--bg3);
	border: 1px solid var(--accent-dim);
	color: var(--yellow);
	border-radius: 3px;
	padding: 10px;
	font-size: 12px;
	letter-spacing: 0.06em;
	cursor: pointer;
	font-family: inherit;
	transition: transform 0.06s ease, background 0.15s ease;
}
.shop-sellall:hover { background: var(--accent-dim); color: var(--text-bright); }
.shop-sellall:active { transform: translateY(1px) scale(0.99); }
.shop2 .noafford { color: var(--red); }

/* Auth screen */
#auth-screen {
	position: fixed;
	inset: 0;
	background: var(--bg);
	z-index: 200;
	display: flex;
	align-items: center;
	justify-content: center;
}

#auth-box {
	background: var(--bg2);
	border: 1px solid var(--border);
	box-shadow: 0 0 24px -8px var(--cyan);
	padding: 24px 28px 32px;
	width: 480px;
	max-width: 92vw;
	border-radius: 2px;
}

#auth-ascii {
	font-family: var(--font-mono);
	font-size: 13px;
	line-height: 1.15;
	white-space: pre;
	text-align: center;
	margin-bottom: 14px;
	user-select: none;
}

/* Two-tone glow — magenta frame, cyan core. Each channel pulses its own
     text-shadow blur on its own animation so the glow itself breathes,
     not just brightness/opacity over a flat color. */
.sk-m {
	color: var(--accent);
	animation: glow-m 3s ease-in-out infinite;
}
.sk-c {
	color: var(--cyan);
	animation: glow-c 3s ease-in-out infinite;
	animation-delay: 0.4s;
}

@keyframes glow-m {
	0%,
	100% {
		text-shadow: 0 0 6px var(--accent-dim);
	}
	50% {
		text-shadow:
			0 0 18px var(--accent),
			0 0 32px var(--accent-dim);
	}
}
@keyframes glow-c {
	0%,
	100% {
		text-shadow: 0 0 6px var(--cyan);
	}
	50% {
		text-shadow:
			0 0 20px var(--cyan),
			0 0 36px var(--cyan);
	}
}

#auth-title {
	font-family: var(--font-mono);
	font-size: 10px;
	line-height: 1.4;
	white-space: pre;
	text-align: center;
	margin-bottom: 6px;
	user-select: none;
	letter-spacing: 0.5px;
}

#auth-box .tagline {
	text-align: center;
	color: var(--text-dim);
	font-size: 11px;
	margin-bottom: 24px;
}

.auth-field {
	margin-bottom: 14px;
}
.auth-field label {
	display: block;
	font-size: 11px;
	color: var(--text-dim);
	text-transform: uppercase;
	letter-spacing: 1px;
	margin-bottom: 4px;
}
.auth-field input {
	width: 100%;
	background: var(--bg3);
	border: 1px solid var(--border);
	color: var(--text-bright);
	font-family: var(--font-mono);
	font-size: 13px;
	padding: 8px 10px;
	outline: none;
	border-radius: 2px;
	transition: border-color 0.15s;
}
.auth-field input:focus {
	border-color: var(--accent);
}

.auth-btn {
	width: 100%;
	background: var(--accent-dim);
	border: 1px solid var(--accent);
	color: var(--text-bright);
	font-family: var(--font-mono);
	font-size: 13px;
	padding: 10px;
	cursor: pointer;
	letter-spacing: 2px;
	text-transform: uppercase;
	transition: all 0.15s;
	border-radius: 2px;
	margin-bottom: 8px;
}
.auth-btn:hover {
	background: var(--accent);
	color: var(--bg);
}

.auth-remember {
	margin-bottom: 12px;
}
.auth-remember label {
	display: flex;
	align-items: center;
	gap: 6px;
	font-size: 11px;
	color: var(--text-dim);
	text-transform: uppercase;
	letter-spacing: 1px;
	cursor: pointer;
}
.auth-remember input[type="checkbox"] {
	accent-color: var(--accent);
	width: 13px;
	height: 13px;
	cursor: pointer;
}

.auth-toggle {
	text-align: center;
	font-size: 11px;
	color: var(--text-dim);
	margin-top: 8px;
}
.auth-toggle a {
	color: var(--accent);
	cursor: pointer;
	text-decoration: none;
}
.auth-toggle a:hover {
	text-decoration: underline;
}

.auth-error {
	color: var(--red);
	font-size: 11px;
	margin-top: 8px;
	text-align: center;
	min-height: 16px;
}

.auth-handle-field {
	display: none;
}
.auth-handle-field.visible {
	display: block;
}

/* Whisper-chat notification badge pulse */
@keyframes chat-ping {
	from {
		transform: scale(1);
		box-shadow: 0 0 4px var(--accent);
	}
	to {
		transform: scale(1.35);
		box-shadow:
			0 0 10px var(--accent),
			0 0 20px var(--accent-dim);
	}
}

.msg-ambient-distant {
	opacity: 0.65;
}

#chat-notif-badge {
	display: none;
	position: absolute;
	top: -7px;
	right: -7px;
	background: var(--red);
	color: #fff;
	font-family: var(--font-mono);
	font-size: 11px;
	font-weight: bold;
	width: 18px;
	height: 18px;
	border-radius: 3px;
	border: 1px solid rgba(255, 255, 255, 0.3);
	align-items: center;
	justify-content: center;
	pointer-events: none;
	animation: chat-badge-spin 3s ease-in-out infinite;
}
@keyframes chat-badge-spin {
	0%,
	60%,
	100% {
		transform: rotateY(0deg);
	}
	70% {
		transform: rotateY(180deg);
	}
	80% {
		transform: rotateY(0deg);
	}
}

@keyframes blink {
	0%, 100% { opacity: 1; }
	50%       { opacity: 0; }
}

/* ─── MOBILE HUD STRIP ─── */
#mobile-hud {
	display: none;
	align-items: stretch;
	gap: 6px;
	padding: 4px 8px;
	background: var(--bg2);
	border-bottom: 1px solid var(--border);
	flex-shrink: 0;
}

[data-density="compact"] #mobile-hud {
	display: flex;
}

@media (max-width: 720px) {
	#mobile-hud {
		display: flex;
	}
}

#mobile-vitals {
	display: flex;
	flex-direction: column;
	gap: 2px;
	flex: 1;
	min-width: 0;
}

.mob-bar-row {
	display: flex;
	align-items: center;
	gap: 3px;
}

.mob-bar-label {
	font-size: 8px;
	color: var(--text-dim);
	width: 14px;
	flex-shrink: 0;
	line-height: 1;
	letter-spacing: 0.3px;
}

.mob-bar-bg {
	position: relative;
	background: var(--bg3);
	height: 10px;
	border-radius: 1px;
	overflow: hidden;
	flex: 1;
}

.mob-bar-fill {
	height: 100%;
	border-radius: 1px;
	transition: width 0.3s;
	display: flex;
	align-items: center;
	justify-content: flex-end;
	padding-right: 3px;
}

.mob-bar-val {
	font-size: 9px;
	color: rgba(0, 0, 0, 0.7);
	line-height: 1;
	pointer-events: none;
	font-weight: bold;
}

/* ─── HUD RIGHT COLUMN (chat button + minimap, map on the right) ─── */
#mob-hud-right {
	display: flex;
	flex-direction: row;
	align-items: center;
	gap: 4px;
	flex-shrink: 0;
}

/* Compact HUD minimap — same edge-to-edge grid, smaller tiles. */
#minimap-grid-hud {
	--mm-room: 1.4em;
	font-size: 8px;
	font-family: var(--font-mono);
	background: var(--bg);
	border: 1px solid var(--border);
	border-radius: 2px;
	padding: 2px 3px;
	cursor: pointer;
	transition: border-color 0.15s;
}
#minimap-grid-hud:active {
	border-color: var(--accent);
}

/* Info stack: weather, body temp, chat — stacked vertically to minimap's right */
#mob-hud-info {
	display: flex;
	flex-direction: column;
	gap: 2px;
	justify-content: space-between;
}

.mob-hud-weather {
	font-size: 13px;
	line-height: 1;
	text-align: center;
}

.mob-hud-temp {
	color: var(--cyan);
	font-size: 9px;
	line-height: 1;
}

.mob-hud-bodytemp {
	color: var(--orange);
	font-size: 9px;
	line-height: 1;
}

#mobile-chat-btn {
	display: none; /* lives in the input row; mobile/compact only */
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 16px;
	padding: 6px 8px;
	cursor: pointer;
	border-radius: 2px;
	flex-shrink: 0;
	transition: all 0.15s;
	-webkit-tap-highlight-color: transparent;
	line-height: 1;
}
@media (max-width: 720px) {
	#mobile-chat-btn { display: block; }
}
[data-density="compact"] #mobile-chat-btn { display: block; }
#mobile-chat-btn:active {
	border-color: var(--accent);
	color: var(--accent);
}

/* ─── AREA TOGGLE BAR (mobile only) ─── */
#area-toggle-bar {
	display: none;
	border-bottom: 1px solid var(--border);
	background: var(--bg2);
	flex-shrink: 0;
}

@media (max-width: 720px) {
	#area-toggle-bar {
		display: flex;
	}
}

[data-density="compact"] #area-toggle-bar {
	display: flex;
}

#area-toggle-btn {
	background: transparent;
	border: none;
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 10px;
	padding: 3px 10px;
	cursor: pointer;
	letter-spacing: 1px;
	text-transform: uppercase;
	width: 100%;
	text-align: left;
	-webkit-tap-highlight-color: transparent;
}
#area-toggle-btn:active {
	color: var(--accent);
}

/* Bottom region wrapper. Desktop: plain vertical stack (cmds bar above input,
   dpad hidden). Mobile/compact: the dpad becomes a full-height column on the
   right while the cmds/weather bar and the input row stack to its left. */
#bottom-input-wrap {
	display: flex;
	flex-direction: column;
}
#bottom-stack {
	display: flex;
	flex-direction: column;
	min-width: 0;
}
@media (max-width: 720px) {
	#bottom-input-wrap { flex-direction: row; align-items: stretch; }
	#bottom-stack { flex: 1; }
	#bottom-input-wrap #mob-dpad { border-left: 1px solid var(--border); }
}
[data-density="compact"] #bottom-input-wrap { flex-direction: row; align-items: stretch; }
[data-density="compact"] #bottom-stack { flex: 1; }
[data-density="compact"] #bottom-input-wrap #mob-dpad { border-left: 1px solid var(--border); }

/* ─── BOTTOM BAR (quick-cmds + dpad) ─── */
#bottom-bar {
	display: flex;
	align-items: stretch;
	border-top: 1px solid var(--border);
	background: var(--bg);
	flex-shrink: 0;
	position: relative;
}

#quick-cmds {
	flex: 1;
	border-top: none;
	border-right: 1px solid var(--border);
	min-width: 0;
}

/* Mobile-only command fan-out trigger + relocated weather. Hidden on desktop,
   where the quick-cmds bar shows inline and weather lives in the sidebar. */
#bottom-left {
	display: none;
	align-items: center;
	gap: 10px;
	padding: 6px 12px;
	flex: 1;
	min-width: 0;
}

#cmd-fan-btn {
	background: var(--bg3);
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 13px;
	padding: 8px 14px;
	min-height: 36px;
	cursor: pointer;
	border-radius: 2px;
	flex-shrink: 0;
	letter-spacing: 1px;
	-webkit-tap-highlight-color: transparent;
	transition: all 0.1s;
}
#cmd-fan-btn:active {
	border-color: var(--accent);
	color: var(--accent);
}

/* The quick-cmds bar and its mobile ≡ fan trigger are retired from the player UI
   — the always-on Smart bar (Tablet + room verbs) is the command surface now.
   Hidden rather than deleted so it's trivially restorable; #bottom-weather still
   shows on mobile since it's a sibling of #cmd-fan-btn, not inside #quick-cmds. */
#quick-cmds,
#cmd-fan-btn {
	display: none !important;
}

#bottom-weather {
	display: flex;
	align-items: center;
	gap: 6px;
	min-width: 0;
}
#bottom-weather .mob-hud-weather { font-size: 16px; }
#bottom-weather .mob-hud-temp,
#bottom-weather .mob-hud-bodytemp { font-size: 11px; }

/* Mobile: command list becomes a fan-out popup above the bar, triggered by
   #cmd-fan-btn; #bottom-left (button + weather) takes its place inline.
   The popup styling is shared by the width media query and compact mode. */
@media (max-width: 720px) {
	#bottom-left { display: flex; }
	#quick-cmds {
		display: none;
		position: absolute;
		left: 0;
		right: 0;
		bottom: 100%;
		flex-wrap: wrap;
		overflow-x: hidden;
		border-right: none;
		border-bottom: 1px solid var(--border);
		box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.4);
		z-index: 60;
	}
	#quick-cmds.open { display: flex; }
}
[data-density="compact"] #bottom-left { display: flex; }
[data-density="compact"] #quick-cmds {
	display: none;
	position: absolute;
	left: 0;
	right: 0;
	bottom: 100%;
	flex-wrap: wrap;
	overflow-x: hidden;
	border-right: none;
	border-bottom: 1px solid var(--border);
	box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.4);
	z-index: 60;
}
[data-density="compact"] #quick-cmds.open { display: flex; }

/* ─── MOBILE DPAD ─── */
#mob-dpad {
	display: none;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 1px;
	flex-shrink: 0;
	padding: 4px 4px;
}

@media (max-width: 720px) {
	#mob-dpad {
		display: flex;
	}
}

[data-density="compact"] #mob-dpad {
	display: flex;
}

.dpad-row {
	display: flex;
	align-items: center;
	gap: 1px;
}

.dpad-center {
	width: 34px;
	height: 34px;
}

/* The centre cell doubles as the size cycler — dim glyph, lights up on press. */
.dpad-size-btn {
	padding: 0;
	background: transparent;
	border: 1px solid transparent;
	color: var(--text-dim);
	opacity: 0.5;
	font-family: var(--font-mono);
	font-size: 13px;
	line-height: 1;
	border-radius: 2px;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}
.dpad-size-btn:hover,
.dpad-size-btn:active {
	opacity: 1;
	color: var(--accent);
	border-color: var(--accent);
}

/* Centre cell of the location d-pad: toggles WASD keyboard movement (desktop). */
.dpad-wasd-btn {
	padding: 0;
	background: transparent;
	border: 1px solid transparent;
	color: var(--text-dim);
	opacity: 0.5;
	font-family: var(--font-mono);
	font-size: 15px;
	line-height: 1;
	border-radius: 2px;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}
.dpad-wasd-btn:hover {
	opacity: 1;
	color: var(--accent);
	border-color: var(--accent);
}
/* Armed — WASD keys now drive movement and hold keyboard focus. */
.dpad-wasd-btn[aria-pressed="true"] {
	opacity: 1;
	color: var(--accent);
	background: var(--accent-dim);
	border-color: var(--accent);
}

.dpad-btn {
	width: 34px;
	height: 34px;
	background: var(--bg3);
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 13px;
	font-weight: bold;
	border-radius: 2px;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: border-color 0.1s, color 0.1s, background 0.1s;
	-webkit-tap-highlight-color: transparent;
	user-select: none;
	/* Press is captured by JS (fire on release); don't let a drag scroll instead. */
	touch-action: none;
}

.dpad-btn:active,
.dpad-btn.dpad-pressing {
	background: var(--accent-dim);
	border-color: var(--accent);
	color: var(--accent);
}

/* Held, but the finger has slid off — releasing now will NOT fire. */
.dpad-btn.dpad-cancel {
	background: var(--bg3);
	border-color: var(--border);
	color: var(--text-dim);
	opacity: 0.45;
}

.dpad-btn.dpad-available {
	border-color: var(--accent);
	color: var(--accent);
	background: var(--accent-dim);
}

/* Locked exit we can't pass — red. Overrides plain available. */
.dpad-btn.dpad-locked {
	border-color: var(--red);
	color: var(--red);
	background: color-mix(in srgb, var(--red) 18%, transparent);
}

/* Locked exit whose lock we control — green. Highest priority. */
.dpad-btn.dpad-owned {
	border-color: var(--green);
	color: var(--green);
	background: color-mix(in srgb, var(--green) 18%, transparent);
}

.dpad-blank {
	background: transparent;
	border-color: transparent;
	cursor: default;
	pointer-events: none;
}

.dpad-ud {
	opacity: 0.6;
	font-size: 12px;
}
.dpad-ud.dpad-available {
	opacity: 1;
}

/* ─── SMART ACTION BAR ─── */
/* Contextual verb row below the command input. Populated by renderSmartBar
   from the current room; always on (every device — data-smart-ui is pinned to
   "on" in applySettings), and hidden only when the room offers nothing. */
/* Registered so the edge-fade widths below can transition smoothly. */
@property --fade-l { syntax: '<length>'; inherits: false; initial-value: 0px; }
@property --fade-r { syntax: '<length>'; inherits: false; initial-value: 0px; }
#smart-bar {
	display: none;
	align-items: center;
	gap: 6px;
	padding: 6px 8px;
	overflow-x: auto;
	border-top: 1px solid var(--border);
	background: var(--bg);
	flex-shrink: 0;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none;
	/* Edge-fade cue that the row swipe-scrolls (no scrollbar). --fade-l/-r are
	   flipped on by .can-scroll-left/right in JS so only the overflowing side
	   fades — the last visible button dissolves off, reading as "more here". */
	--fade-l: 0px;
	--fade-r: 0px;
	-webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-l), #000 calc(100% - var(--fade-r)), transparent 100%);
	mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-l), #000 calc(100% - var(--fade-r)), transparent 100%);
	transition: --fade-l 0.15s, --fade-r 0.15s;
}
#smart-bar::-webkit-scrollbar { display: none; }
#smart-bar.can-scroll-left { --fade-l: 30px; }
#smart-bar.can-scroll-right { --fade-r: 30px; }

[data-smart-ui="on"] #smart-bar { display: flex; }
[data-smart-ui="on"] #smart-bar.smart-bar-empty { display: none; }

.smart-btn {
	background: var(--bg3);
	border: 1px solid var(--accent);
	color: var(--accent);
	font-family: var(--font-mono);
	font-size: 13px;
	font-weight: bold;
	padding: 8px 14px;
	min-height: 38px;
	border-radius: 2px;
	cursor: pointer;
	flex-shrink: 0;
	white-space: nowrap;
	letter-spacing: 0.5px;
	-webkit-tap-highlight-color: transparent;
	transition: background 0.1s, border-color 0.1s, color 0.1s;
}
.smart-btn:active {
	background: var(--accent-dim);
}
/* Lifted by a long-press for drag-to-reorder. */
.smart-btn-dragging {
	opacity: 0.55;
	transform: scale(1.06);
	box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
	cursor: grabbing;
}
/* The Tablet anchor — a touch brighter than the room verbs: accent-tinted fill,
   lighter border, and a soft glow so it reads as the always-there primary. */
.smart-btn-accent {
	background: color-mix(in srgb, var(--accent) 22%, var(--bg3));
	border-color: color-mix(in srgb, var(--accent) 100%, white 25%);
	box-shadow: 0 0 8px color-mix(in srgb, var(--accent) 40%, transparent);
}
.smart-btn-accent:active {
	background: var(--accent);
	color: var(--bg);
}

/* Player macro buttons — a distinct hue from the magenta anchors/verbs so saved
   command shortcuts read apart. --macro-color defaults here but is overridden
   inline per-macro when the player picks a colour. */
.smart-btn-macro {
	--macro-color: #2ee6ff;
	background: color-mix(in srgb, var(--macro-color) 14%, var(--bg3));
	border-color: var(--macro-color);
	color: var(--macro-color);
}
.smart-btn-macro:active {
	background: var(--macro-color);
	color: var(--bg);
}
.smart-btn-macro-add {
	opacity: 0.8;
	font-weight: bold;
	padding-left: 12px;
	padding-right: 12px;
}

/* Stop chip — shown only while a macro is running, pinned to the far left as the
   escape hatch. Alarm-red with a soft pulse so it draws the eye without shouting. */
.smart-btn-macro-stop {
	--stop-color: #ff4d4d;
	background: color-mix(in srgb, var(--stop-color) 18%, var(--bg3));
	border-color: var(--stop-color);
	color: var(--stop-color);
	font-weight: bold;
	animation: smart-stop-pulse 1.4s ease-in-out infinite;
}
.smart-btn-macro-stop:active {
	background: var(--stop-color);
	color: var(--bg);
}
@keyframes smart-stop-pulse {
	0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--stop-color) 55%, transparent); }
	50% { box-shadow: 0 0 6px 1px color-mix(in srgb, var(--stop-color) 45%, transparent); }
}

/* Macro manager / editor modal. Undimmed backdrop; fixed size that matches the
   Guide window so they line up when the Guide layers over it. */
.smart-macro-overlay {
	position: fixed;
	inset: 0;
	z-index: 210;
	background: transparent;
	display: flex;
	align-items: center;
	justify-content: center;
}
.smart-macro-box {
	width: 100%;
	max-width: 460px;
	height: min(82vh, 560px);
	overflow: hidden;
	background: var(--bg);
	border: 1px solid var(--border);
	box-shadow: 0 6px 28px rgba(0, 0, 0, 0.55);
	border-radius: 3px;
	display: flex;
	flex-direction: column;
}
.smart-macro-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 12px 14px;
	border-bottom: 1px solid var(--border);
	font-family: var(--font-mono);
	font-weight: bold;
	letter-spacing: 1px;
	text-transform: uppercase;
	color: var(--text-dim);
	flex-shrink: 0;
}
.smart-macro-x {
	background: transparent;
	border: none;
	color: var(--text-dim);
	font-size: 16px;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.smart-macro-body {
	flex: 1;
	min-height: 0;
	overflow-y: auto;
	padding: 12px 14px;
	display: flex;
	flex-direction: column;
	gap: 12px;
	scrollbar-width: thin;
	scrollbar-color: var(--border) var(--bg2);
}
.smart-macro-body::-webkit-scrollbar { width: 6px; }
.smart-macro-body::-webkit-scrollbar-track { background: var(--bg2); }
.smart-macro-body::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
/* Keep fields (esp. the tall commands box) at natural height; scroll, don't squash. */
.smart-macro-body > * { flex-shrink: 0; }
.smart-macro-list {
	display: flex;
	flex-direction: column;
	gap: 6px;
}
.smart-macro-row {
	display: flex;
	gap: 6px;
}
.smart-macro-name {
	--macro-color: #2ee6ff;
	flex: 1;
	text-align: left;
	background: var(--bg3);
	border: 1px solid var(--macro-color);
	color: var(--macro-color);
	font-family: var(--font-mono);
	font-size: 14px;
	padding: 10px 12px;
	min-height: 40px;
	border-radius: 2px;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.smart-macro-del {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-size: 13px;
	padding: 0 12px;
	border-radius: 2px;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.smart-macro-form {
	display: flex;
	flex-direction: column;
	gap: 12px;
}
.smart-macro-field {
	display: flex;
	flex-direction: column;
	gap: 4px;
}
.smart-macro-label {
	font-family: var(--font-mono);
	font-size: 12px;
	color: var(--text-dim);
	letter-spacing: 0.5px;
	text-transform: uppercase;
}
.smart-macro-input {
	background: var(--bg3);
	border: 1px solid var(--border);
	color: var(--text);
	font-family: var(--font-mono);
	font-size: 14px;
	padding: 8px 10px;
	border-radius: 2px;
	width: 100%;
	box-sizing: border-box;
}
.smart-macro-input:focus {
	outline: none;
	border-color: var(--accent);
}
.smart-macro-textarea {
	resize: vertical;
	min-height: 200px;
	line-height: 1.5;
}
.smart-macro-hint {
	font-family: var(--font-mono);
	font-size: 11px;
	color: var(--text-dim);
	line-height: 1.4;
}
.smart-macro-colorrow {
	display: flex;
	align-items: center;
	gap: 10px;
}
.smart-macro-color {
	width: 26px;
	height: 26px;
	padding: 1px;
	border: 1px solid var(--border);
	border-radius: 2px;
	background: var(--bg3);
	cursor: pointer;
	flex-shrink: 0;
	-webkit-tap-highlight-color: transparent;
}
/* Quick-pick chips of colours already used by existing macros. */
.smart-macro-used {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin-top: 8px;
}
.smart-macro-usedchip {
	width: 26px;
	height: 26px;
	border: 1px solid var(--border);
	border-radius: 2px;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.smart-macro-usedchip.on {
	border-color: var(--text);
	box-shadow: 0 0 0 2px var(--text) inset;
}
/* Live preview of the button in the chosen colour. */
.smart-macro-preview {
	--macro-color: #2ee6ff;
	flex: 1;
	text-align: center;
	background: color-mix(in srgb, var(--macro-color) 14%, var(--bg3));
	border: 1px solid var(--macro-color);
	color: var(--macro-color);
	font-family: var(--font-mono);
	font-size: 13px;
	font-weight: bold;
	letter-spacing: 0.5px;
	padding: 8px 12px;
	min-height: 36px;
	border-radius: 2px;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.smart-macro-foot {
	display: flex;
	gap: 6px;
	padding: 12px 14px;
	border-top: 1px solid var(--border);
	flex-shrink: 0;
}
.smart-macro-cancel {
	flex: 1;
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 14px;
	padding: 10px 14px;
	min-height: 42px;
	border-radius: 2px;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.smart-macro-save {
	flex: 2;
	background: color-mix(in srgb, var(--accent) 22%, var(--bg3));
	border: 1px solid var(--accent);
	color: var(--accent);
	font-family: var(--font-mono);
	font-size: 14px;
	font-weight: bold;
	padding: 10px 14px;
	min-height: 42px;
	border-radius: 2px;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.smart-macro-save:active {
	background: var(--accent);
	color: var(--bg);
}
.smart-macro-save:disabled {
	opacity: 0.4;
	cursor: default;
	background: var(--bg3);
}
/* Test row — validate the macro before it can be added. Pinned above the footer,
   outside the scrolling body. */
.smart-macro-testrow {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 10px 14px;
	border-top: 1px solid var(--border);
	flex-shrink: 0;
	flex-wrap: wrap;
}
.smart-macro-test {
	background: transparent;
	border: 1px solid var(--accent);
	color: var(--accent);
	font-family: var(--font-mono);
	font-size: 13px;
	font-weight: bold;
	padding: 8px 16px;
	min-height: 36px;
	border-radius: 2px;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
	flex-shrink: 0;
}
.smart-macro-test:active { background: var(--accent-dim); }
.smart-macro-testmsg {
	font-family: var(--font-mono);
	font-size: 12px;
	line-height: 1.35;
	color: var(--text-dim);
}
.smart-macro-testmsg.ok { color: #6ee87a; }
.smart-macro-testmsg.err { color: #ff5a6e; }
.smart-macro-testmsg.warn { color: #e8c65a; }
.smart-macro-hint code {
	background: var(--bg3);
	padding: 1px 4px;
	border-radius: 2px;
	color: var(--text);
}
.smart-macro-guide {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 13px;
	padding: 8px 14px;
	min-height: 36px;
	border-radius: 2px;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
	flex-shrink: 0;
}
.smart-macro-guide:active { background: var(--bg3); }

/* Guide window — layered reference over the editor; click a row to insert it.
   Sized to match the macro window and left undimmed so the editor stays visible
   behind it. */
.smart-guide-overlay {
	position: fixed;
	inset: 0;
	z-index: 220;
	background: transparent;
	display: flex;
	align-items: center;
	justify-content: center;
}
.smart-guide-box {
	width: 100%;
	max-width: 460px;
	height: min(82vh, 560px);
	display: flex;
	flex-direction: column;
	background: var(--bg);
	border: 1px solid var(--border);
	box-shadow: 0 6px 28px rgba(0, 0, 0, 0.6);
	border-radius: 3px;
	overflow: hidden;
}
.smart-guide-tabs {
	display: flex;
	border-bottom: 1px solid var(--border);
	flex-shrink: 0;
	flex-wrap: wrap;
}
.smart-guide-tab {
	flex: 1;
	background: var(--bg3);
	border: none;
	border-right: 1px solid var(--border);
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 11px;
	font-weight: bold;
	letter-spacing: 0.3px;
	padding: 10px 3px;
	min-height: 40px;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.smart-guide-tab:last-child { border-right: none; }
.smart-guide-tab.on {
	background: var(--bg);
	color: var(--accent);
	box-shadow: inset 0 -2px 0 var(--accent);
}
.smart-guide-content {
	flex: 1;
	min-height: 0;
	overflow-y: auto;
	padding: 8px;
	display: flex;
	flex-direction: column;
	gap: 4px;
	scrollbar-width: thin;
	scrollbar-color: var(--border) var(--bg2);
}
.smart-guide-content::-webkit-scrollbar { width: 6px; }
.smart-guide-content::-webkit-scrollbar-track { background: var(--bg2); }
.smart-guide-content::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
/* Column flex would otherwise shrink each row/example to fit and clip its text —
   pin natural heights so the container scrolls instead. */
.smart-guide-content > * { flex-shrink: 0; }
.smart-guide-note {
	font-family: var(--font-mono);
	font-size: 11px;
	line-height: 1.4;
	color: var(--text-dim);
	padding: 4px 6px 8px;
}
.smart-guide-para {
	font-family: var(--font-mono);
	font-size: 12px;
	line-height: 1.5;
	color: var(--text);
	padding: 2px 6px 4px;
}
.smart-guide-ex {
	font-family: var(--font-mono);
	font-size: 12px;
	line-height: 1.5;
	color: var(--accent);
	background: var(--bg3);
	border: 1px solid var(--border);
	border-radius: 2px;
	margin: 2px 6px 6px;
	padding: 8px 10px;
	overflow-x: auto;
	white-space: pre;
}
.smart-guide-group {
	font-family: var(--font-mono);
	font-size: 11px;
	font-weight: bold;
	letter-spacing: 1px;
	text-transform: uppercase;
	color: var(--text-dim);
	padding: 8px 6px 2px;
}
.smart-guide-row {
	display: flex;
	align-items: baseline;
	gap: 10px;
	text-align: left;
	background: transparent;
	border: 1px solid transparent;
	border-radius: 2px;
	padding: 7px 8px;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.smart-guide-row:hover { background: var(--bg3); border-color: var(--border); }
.smart-guide-tok {
	font-family: var(--font-mono);
	font-size: 13px;
	color: var(--accent);
	white-space: nowrap;
	flex-shrink: 0;
}
.smart-guide-desc {
	font-family: var(--font-mono);
	font-size: 11px;
	color: var(--text-dim);
	overflow: hidden;
	text-overflow: ellipsis;
}

/* Bottom-sheet target chooser (opened when a verb has >1 target). */
.smart-sheet-overlay {
	position: fixed;
	inset: 0;
	z-index: 200;
	background: rgba(0, 0, 0, 0.55);
	display: flex;
	align-items: flex-end;
	justify-content: center;
}
.smart-sheet {
	width: 100%;
	max-width: 520px;
	max-height: 70vh;
	overflow-y: auto;
	background: var(--bg);
	border: 1px solid var(--border);
	border-bottom: none;
	box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.5);
	padding: 12px;
	display: flex;
	flex-direction: column;
	gap: 8px;
}
.smart-sheet-title {
	font-family: var(--font-mono);
	font-size: 13px;
	font-weight: bold;
	color: var(--text-dim);
	letter-spacing: 1px;
	text-transform: uppercase;
	padding: 2px 4px 6px;
}
.smart-sheet-list {
	display: flex;
	flex-direction: column;
	gap: 6px;
}
.smart-sheet-item {
	background: var(--bg3);
	border: 1px solid var(--accent);
	color: var(--accent);
	font-family: var(--font-mono);
	font-size: 15px;
	text-align: left;
	padding: 12px 14px;
	min-height: 44px;
	border-radius: 2px;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.smart-sheet-item:active { background: var(--accent-dim); }
/* Pager row: Prev / page indicator / Next — only shown when a list paginates. */
.smart-sheet-pager {
	display: flex;
	align-items: center;
	gap: 6px;
}
.smart-sheet-page {
	flex: 1;
	background: var(--bg3);
	border: 1px solid var(--border);
	color: var(--accent);
	font-family: var(--font-mono);
	font-size: 14px;
	padding: 10px 14px;
	min-height: 44px;
	border-radius: 2px;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.smart-sheet-page:disabled {
	color: var(--text-dim);
	opacity: 0.4;
	cursor: default;
}
.smart-sheet-page-ind {
	font-family: var(--font-mono);
	font-size: 13px;
	color: var(--text-dim);
	min-width: 40px;
	text-align: center;
}
/* Nav row: Back (when drilled in) + Cancel, side by side. */
.smart-sheet-nav {
	display: flex;
	gap: 6px;
	margin-top: 4px;
}
.smart-sheet-nav .smart-sheet-cancel {
	flex: 1;
	margin-top: 0;
}
.smart-sheet-cancel {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 14px;
	padding: 10px 14px;
	min-height: 44px;
	border-radius: 2px;
	cursor: pointer;
	margin-top: 4px;
	-webkit-tap-highlight-color: transparent;
}

/* D-pad size variants — driven by data-dpad-size on <html> */
[data-dpad-size="medium"] .dpad-btn,
[data-dpad-size="medium"] .dpad-center {
	width: 42px;
	height: 42px;
	font-size: 16px;
}
[data-dpad-size="medium"] .dpad-ud { font-size: 14px; }

[data-dpad-size="large"] .dpad-btn,
[data-dpad-size="large"] .dpad-center {
	width: 52px;
	height: 52px;
	font-size: 20px;
}
[data-dpad-size="large"] .dpad-ud { font-size: 17px; }

/* Guard: on very narrow screens, cap "large" at the medium footprint so the
   command input can't get cramped. Mirrors the medium tier; must follow the
   large block to win on equal specificity. */
@media (max-width: 360px) {
	[data-dpad-size="large"] .dpad-btn,
	[data-dpad-size="large"] .dpad-center {
		width: 42px;
		height: 42px;
		font-size: 16px;
	}
	[data-dpad-size="large"] .dpad-ud { font-size: 14px; }
}

/* Hide d-pad size setting on non-touch (desktop) */
@media (hover: hover) and (pointer: fine) {
	.mobile-only-setting { display: none; }
}

/* Hide desktop-only settings on touch/mobile */
@media (hover: none), (pointer: coarse) {
	.desktop-only-setting { display: none; }
}

#mobile-minimap-panel {
	display: none;
	position: fixed;
	width: fit-content;
	background: var(--bg2);
	border: 1px solid var(--border);
	border-radius: 4px;
	z-index: 95;
	cursor: grab;
	touch-action: none;
	flex-direction: column;
	align-items: center;
	padding: 6px 8px 8px;
	gap: 6px;
	box-shadow: -4px 0 16px rgba(0, 0, 0, 0.4);
}
#mobile-minimap-panel.open {
	display: flex;
}

#minimap-grid-mob {
	--mm-room: 1.75em;
	font-size: 11px;
	font-family: var(--font-mono);
}

#mobile-map-close {
	background: transparent;
	border: none;
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 11px;
	padding: 0;
	cursor: pointer;
	line-height: 1;
	align-self: flex-end;
	transition: color 0.15s;
}
#mobile-map-close:hover {
	color: var(--red);
}

.whisper-tab {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 10px;
	padding: 3px 8px;
	cursor: pointer;
	border-radius: 2px;
	white-space: nowrap;
	flex-shrink: 0;
	position: relative;
}
.whisper-tab.active { background: var(--bg3); }
.whisper-tab.active.tab-purple { border-color: var(--purple); color: var(--purple); }
.whisper-tab.active.tab-yellow { border-color: var(--yellow); color: var(--yellow); }
.whisper-tab-wrap {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	flex-shrink: 0;
	position: relative;
	background: transparent;
	border: 1px solid var(--border);
	border-radius: 2px;
	padding: 2px 4px 2px 8px;
	cursor: pointer;
}
.whisper-tab-wrap.active { background: var(--bg3); border-color: var(--accent); }
.whisper-tab-wrap.active .whisper-tab-label { color: var(--accent); }
.whisper-tab-label {
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 10px;
	white-space: nowrap;
}
.whisper-tab-pip {
	position: absolute;
	top: -5px;
	left: -5px;
	background: var(--red);
	color: #fff;
	font-size: 9px;
	font-weight: bold;
	width: 12px;
	height: 12px;
	border-radius: 2px;
	display: flex;
	align-items: center;
	justify-content: center;
	pointer-events: none;
}
.whisper-tab-close {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-family: var(--font-mono);
	font-size: 11px;
	line-height: 1;
	width: 16px;
	height: 16px;
	padding: 0;
	cursor: pointer;
	border-radius: 2px;
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
}
.whisper-tab-close:hover { border-color: var(--red); color: var(--red); }
#whisper-reply-input::placeholder { color: var(--text-dim); }
#whisper-reply-input:focus { border-color: var(--accent); }
#whisper-new-msgs {
	display: none;
	position: absolute;
	bottom: 50px;
	right: 8px;
	background: var(--accent);
	color: var(--accent-ink);
	font-size: 10px;
	padding: 3px 8px;
	border-radius: 10px;
	cursor: pointer;
	z-index: 101;
}
#whisper-scroll-bottom {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-size: 14px;
	padding: 3px 7px;
	cursor: pointer;
	border-radius: 2px;
	flex-shrink: 0;
	line-height: 1;
	margin-left: 4px;
}
#whisper-log::-webkit-scrollbar {
	width: 6px;
}
#whisper-log::-webkit-scrollbar-track {
	background: var(--bg2);
}
#whisper-log::-webkit-scrollbar-thumb {
	background: var(--border);
	border-radius: 3px;
}

/* ── ATM Panel ── */
#atm-panel {
	display: none;
	position: fixed;
	inset: 0;
	background: transparent;
	z-index: 200;
	align-items: center;
	justify-content: center;
}
#atm-panel.active {
	display: flex;
}

/* Chassis — a moulded terminal housing, not a flat bordered box. Accent is
   network-branded: atm.js sets --atm-accent from network.color, and every
   colour below derives from it via color-mix, so a red bank and a green bank
   render as genuinely different machines. */
#atm-box {
	--atm-accent: #00ff88;
	width: 340px;
	background: linear-gradient(180deg, #2a2f33 0%, #1c2023 7%, #14181b 12%, #0d1013 100%);
	border: 1px solid #05070a;
	border-radius: 20px;
	box-shadow:
		inset 0 1px 0 rgba(255,255,255,0.06),
		inset 0 0 0 1px rgba(0,0,0,0.5),
		0 18px 50px rgba(0,0,0,0.7),
		0 0 40px color-mix(in srgb, var(--atm-accent) 18%, transparent);
	font-family: 'Courier New', Courier, monospace;
	color: var(--atm-accent);
	padding: 14px 16px 16px;
	position: relative;
}
/* Chassis head — brand plate + close, with moulded screws. */
#atm-chassis-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	margin-bottom: 12px;
	padding: 2px 2px 0;
}
.atm-brandplate { display: flex; align-items: center; gap: 8px; }
.atm-brand-mark {
	font-size: 15px;
	color: var(--atm-accent);
	text-shadow: 0 0 8px color-mix(in srgb, var(--atm-accent) 70%, transparent);
}
.atm-brand-text { display: flex; flex-direction: column; line-height: 1.15; }
#atm-network-name {
	font-size: 13px;
	font-weight: bold;
	letter-spacing: 2px;
	color: var(--atm-accent);
	text-shadow: 0 0 6px color-mix(in srgb, var(--atm-accent) 45%, transparent);
}
.atm-headbolts { display: flex; align-items: center; gap: 9px; }
/* Phillips-head screw, moulded into the chassis. */
.atm-screw {
	width: 8px; height: 8px;
	border-radius: 50%;
	background: radial-gradient(circle at 35% 30%, #6b7075, #2a2d30 70%, #16181a);
	box-shadow: inset 0 0 0 1px rgba(0,0,0,0.5), 0 1px 0 rgba(255,255,255,0.05);
	position: relative;
	flex-shrink: 0;
}
.atm-screw::after {
	content: '';
	position: absolute;
	inset: 2px 1px;
	border-top: 1px solid rgba(0,0,0,0.55);
	transform: rotate(35deg);
}
#atm-close {
	background: none;
	border: none;
	color: var(--text-dim, #7a8a83);
	font-size: 14px;
	cursor: pointer;
	padding: 0 2px;
	line-height: 1;
	font-family: inherit;
}
#atm-close:hover { color: #ff4444; }

/* CRT bezel — recessed dark frame that cradles the tube. */
#atm-crt-bezel {
	position: relative;
	background: linear-gradient(160deg, #0a0c0e, #05070a);
	border-radius: 14px;
	padding: 16px;
	box-shadow:
		inset 0 2px 6px rgba(0,0,0,0.9),
		inset 0 0 0 1px rgba(255,255,255,0.03);
}
#atm-crt-bezel .atm-screw { position: absolute; width: 7px; height: 7px; }
.atm-screw-tl { top: 7px; left: 7px; }
.atm-screw-tr { top: 7px; right: 7px; }
.atm-screw-bl { bottom: 7px; left: 7px; }
.atm-screw-br { bottom: 7px; right: 7px; }

/* The tube — bulged glass faked with an elliptical radius + corner vignette. */
#atm-crt {
	position: relative;
	height: 288px;
	border-radius: 26px / 18px;
	overflow: hidden;
	background: radial-gradient(130% 130% at 50% 42%,
		color-mix(in srgb, var(--atm-accent) 9%, #030a07) 55%, #020604 100%);
	box-shadow:
		inset 0 0 30px rgba(0,0,0,0.95),
		inset 0 0 6px color-mix(in srgb, var(--atm-accent) 30%, transparent),
		0 0 0 1px rgba(0,0,0,0.8);
}
#atm-box.atm-dark #atm-crt {
	background: radial-gradient(130% 130% at 50% 42%, #0a0505 55%, #020202 100%);
}
/* Screen content sits above the tube glow, below the scanlines + glass. */
#atm-crt-screen {
	position: absolute;
	inset: 0;
	z-index: 2;
	padding: 14px 16px;
	display: flex;
	flex-direction: column;
	color: var(--atm-accent);
	text-shadow: 0 0 5px color-mix(in srgb, var(--atm-accent) 55%, transparent);
	overflow-y: auto;
	overflow-x: hidden;
	scrollbar-width: none;
}
#atm-crt-screen::-webkit-scrollbar { width: 0; height: 0; }
.atm-crt-scanlines {
	position: absolute;
	inset: 0;
	z-index: 3;
	border-radius: inherit;
	pointer-events: none;
	background: repeating-linear-gradient(0deg, transparent 0 2px, rgba(0,0,0,0.16) 2px 3px);
}
/* Glass — corner vignette + a diagonal sheen streak (borrowed from the AMP deck). */
.atm-crt-glass {
	position: absolute;
	inset: 0;
	z-index: 4;
	border-radius: inherit;
	pointer-events: none;
	background:
		linear-gradient(115deg,
			transparent 0 40%,
			rgba(220,255,245,0.10) 47%, rgba(220,255,245,0.03) 52%,
			transparent 60% 100%),
		radial-gradient(80% 55% at 26% 18%, rgba(255,255,255,0.12), transparent 60%),
		radial-gradient(120% 120% at 50% 50%, transparent 62%, rgba(0,0,0,0.55) 100%);
	box-shadow: inset 0 0 22px rgba(0,0,0,0.6);
}

/* ── CRT screen contents (rendered by atm.js) ── */
.atm-scr-top {
	display: flex;
	align-items: center;
	justify-content: space-between;
	font-size: 10px;
	letter-spacing: 1px;
	margin-bottom: 8px;
}
.atm-scr-title { font-weight: bold; letter-spacing: 2px; font-size: 11px; }
.atm-scr-online { font-size: 8px; letter-spacing: 1px; opacity: 0.85; animation: atm-flicker 8s infinite; }
.atm-scr-bal {
	border-top: 1px dashed color-mix(in srgb, var(--atm-accent) 30%, transparent);
	border-bottom: 1px dashed color-mix(in srgb, var(--atm-accent) 30%, transparent);
	padding: 7px 0;
	margin-bottom: 9px;
}
.atm-scr-balrow {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	font-size: 10px;
	letter-spacing: 1px;
	margin: 2px 0;
}
.atm-scr-balrow span { opacity: 0.55; }
.atm-scr-balrow b { font-size: 13px; }

/* Home menu */
.atm-menu { display: flex; flex-direction: column; gap: 5px; }
.atm-menu-item {
	display: flex;
	align-items: center;
	gap: 8px;
	background: color-mix(in srgb, var(--atm-accent) 6%, transparent);
	border: 1px solid color-mix(in srgb, var(--atm-accent) 25%, transparent);
	border-radius: 4px;
	color: var(--atm-accent);
	font-family: inherit;
	font-size: 11px;
	letter-spacing: 1.5px;
	padding: 6px 10px;
	cursor: pointer;
	text-align: left;
	text-shadow: inherit;
	transition: background 0.12s, border-color 0.12s, box-shadow 0.12s;
}
.atm-menu-item:hover:not(:disabled) {
	background: color-mix(in srgb, var(--atm-accent) 16%, transparent);
	border-color: var(--atm-accent);
	box-shadow: 0 0 12px color-mix(in srgb, var(--atm-accent) 30%, transparent);
}
.atm-menu-item:disabled { opacity: 0.35; cursor: not-allowed; }
.atm-menu-key { opacity: 0.8; }
.atm-menu-hint { margin-left: auto; font-size: 8px; letter-spacing: 1px; opacity: 0.45; }
.atm-menu-danger { color: #ff3b5c; border-color: rgba(255,59,92,0.35); }
.atm-menu-danger:hover:not(:disabled) {
	background: rgba(255,59,92,0.12);
	border-color: #ff3b5c;
	box-shadow: 0 0 12px rgba(255,59,92,0.3);
}
/* Unlocked MAINTENANCE entry — pulses in the network's accent colour rather
   than the flat danger red, so it reads as "newly unlocked", not "hazard". */
.atm-menu-glow {
	border-color: var(--atm-accent);
	background: color-mix(in srgb, var(--atm-accent) 14%, transparent);
	animation: atm-menu-glow-pulse 1.6s ease-in-out infinite;
}
.atm-menu-glow:hover:not(:disabled) {
	background: color-mix(in srgb, var(--atm-accent) 24%, transparent);
	box-shadow: 0 0 16px color-mix(in srgb, var(--atm-accent) 50%, transparent);
}
@keyframes atm-menu-glow-pulse {
	0%, 100% { box-shadow: 0 0 6px color-mix(in srgb, var(--atm-accent) 25%, transparent); }
	50% { box-shadow: 0 0 16px color-mix(in srgb, var(--atm-accent) 55%, transparent); }
}

/* Transaction / account sub-screens */
.atm-back {
	background: none;
	border: none;
	color: var(--atm-accent);
	font-family: inherit;
	font-size: 9px;
	letter-spacing: 1px;
	cursor: pointer;
	opacity: 0.7;
	padding: 0;
}
.atm-back:hover { opacity: 1; }
.atm-scr-avail { font-size: 10px; letter-spacing: 1px; opacity: 0.6; margin-bottom: 10px; }
.atm-input-row { display: flex; gap: 8px; margin-bottom: 8px; }
#atm-amount {
	flex: 1;
	min-width: 0;
	background: color-mix(in srgb, var(--atm-accent) 8%, #000);
	border: 1px solid color-mix(in srgb, var(--atm-accent) 40%, transparent);
	color: var(--atm-accent);
	font-family: inherit;
	font-size: 14px;
	letter-spacing: 1px;
	padding: 7px 10px;
	border-radius: 3px;
	outline: none;
}
#atm-amount::placeholder { color: color-mix(in srgb, var(--atm-accent) 30%, transparent); }
#atm-amount:focus { border-color: var(--atm-accent); box-shadow: 0 0 12px color-mix(in srgb, var(--atm-accent) 35%, transparent); }
#atm-amount::-webkit-outer-spin-button,
#atm-amount::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.atm-key-btn {
	background: transparent;
	border: 1px solid color-mix(in srgb, var(--atm-accent) 35%, transparent);
	color: color-mix(in srgb, var(--atm-accent) 70%, transparent);
	font-family: inherit;
	font-size: 10px;
	letter-spacing: 1px;
	padding: 0 12px;
	border-radius: 3px;
	cursor: pointer;
}
.atm-key-btn:hover { border-color: var(--atm-accent); color: var(--atm-accent); }
.atm-confirm {
	margin-top: auto;
	background: linear-gradient(180deg, color-mix(in srgb, var(--atm-accent) 22%, transparent), color-mix(in srgb, var(--atm-accent) 8%, transparent));
	border: 1px solid var(--atm-accent);
	color: var(--atm-accent);
	font-family: inherit;
	font-size: 11px;
	letter-spacing: 2px;
	padding: 9px;
	border-radius: 4px;
	cursor: pointer;
	box-shadow: inset 0 -2px 0 rgba(0,0,0,0.4);
	transition: filter 0.12s, box-shadow 0.12s, transform 0.05s;
}
.atm-confirm:hover { filter: brightness(1.2); box-shadow: 0 0 14px color-mix(in srgb, var(--atm-accent) 40%, transparent), inset 0 -2px 0 rgba(0,0,0,0.4); }
.atm-confirm:active { transform: translateY(1px); }
.atm-confirm:disabled { opacity: 0.35; cursor: not-allowed; }
.atm-confirm-danger {
	background: linear-gradient(180deg, rgba(255,59,92,0.28), rgba(255,59,92,0.1));
	border-color: #ff3b5c;
	color: #ff3b5c;
}
.atm-confirm-danger:hover:not(:disabled) { box-shadow: 0 0 14px rgba(255,59,92,0.4), inset 0 -2px 0 rgba(0,0,0,0.4); }
.atm-scr-fee { font-size: 9px; letter-spacing: 1px; opacity: 0.5; margin-bottom: 6px; }
.atm-scr-reserve { margin-top: 4px; }
.atm-scr-reserve-lbl { font-size: 9px; letter-spacing: 2px; opacity: 0.55; margin-bottom: 5px; }
.atm-scr-reserve-val { font-size: 9px; letter-spacing: 1px; opacity: 0.7; margin-top: 5px; text-align: right; }

/* Offline / damaged CRT */
.atm-crt-offline { margin: auto; text-align: center; color: #ff3b5c; text-shadow: 0 0 8px rgba(255,59,92,0.5); }
.atm-off-glyph { font-size: 30px; margin-bottom: 8px; }
.atm-off-msg { font-size: 10px; letter-spacing: 2px; white-space: pre-line; line-height: 1.6; }

/* Physical deck below the tube — power LED, card slot, cash tray. */
#atm-deck { display: flex; align-items: center; gap: 10px; margin-top: 12px; padding: 0 2px; }
.atm-deck-led { display: flex; align-items: center; gap: 6px; font-size: 8px; letter-spacing: 1px; color: var(--text-dim, #7a8a83); }
.atm-led {
	width: 8px; height: 8px;
	border-radius: 50%;
	background: var(--atm-accent);
	box-shadow: 0 0 6px var(--atm-accent), 0 0 2px #fff;
	animation: atm-led-pulse 2.4s ease-in-out infinite;
}
.atm-led-off { background: #552027; box-shadow: none; animation: none; }
@keyframes atm-led-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }
.atm-cardslot {
	flex: 1;
	position: relative;
	font-size: 7px;
	letter-spacing: 2px;
	color: rgba(255,255,255,0.28);
	text-align: center;
	padding: 8px 0 5px;
	border-radius: 4px;
	background: linear-gradient(180deg, #05070a, #0d1013);
	box-shadow: inset 0 1px 3px rgba(0,0,0,0.8);
}
.atm-cardslot-lip {
	position: absolute;
	top: 4px; left: 14%; right: 14%;
	height: 3px;
	border-radius: 3px;
	background: #000;
	box-shadow: inset 0 1px 2px rgba(0,0,0,0.9), 0 1px 0 rgba(255,255,255,0.05);
}
.atm-cashtray {
	font-size: 7px;
	letter-spacing: 2px;
	color: rgba(255,255,255,0.28);
	padding: 6px 10px;
	border-radius: 4px;
	background: linear-gradient(180deg, #05070a, #0d1013);
	box-shadow: inset 0 2px 5px rgba(0,0,0,0.85);
}
.atm-logo {
	font-size: 11px;
	letter-spacing: 4px;
	opacity: 0.5;
	margin-bottom: 4px;
}
.atm-subtitle {
	font-size: 9px;
	letter-spacing: 3px;
	opacity: 0.45;
	margin-top: 3px;
}
@keyframes atm-flicker {
	0%, 92%, 94%, 96%, 100% { opacity: 1; }
	93% { opacity: 0.7; }
	95% { opacity: 0.9; }
}

/* ── Halcyon Assurance Panel — same CRT chassis language as the ATM, own dedicated
   markup/CSS (see atm.js's header comment for why: distinct machines, not shared
   classes), themed in the insurer's amber. ── */
#insurance-panel {
	display: none;
	position: fixed;
	inset: 0;
	background: transparent;
	z-index: 200;
	align-items: center;
	justify-content: center;
}
#insurance-panel.active { display: flex; }
#ins-box {
	--ins-accent: var(--yellow);
	width: 360px;
	background: linear-gradient(180deg, #2a2622 0%, #1c1a17 7%, #14120f 12%, #0d0c0a 100%);
	border: 1px solid #05070a;
	border-radius: 20px;
	box-shadow:
		inset 0 1px 0 rgba(255,255,255,0.06),
		inset 0 0 0 1px rgba(0,0,0,0.5),
		0 18px 50px rgba(0,0,0,0.7),
		0 0 40px color-mix(in srgb, var(--ins-accent) 18%, transparent);
	font-family: 'Courier New', Courier, monospace;
	color: var(--ins-accent);
	padding: 14px 16px 16px;
	position: relative;
}
#ins-chassis-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	margin-bottom: 12px;
	padding: 2px 2px 0;
}
.ins-brandplate { display: flex; align-items: center; gap: 8px; }
.ins-brand-mark { font-size: 15px; color: var(--ins-accent); text-shadow: 0 0 8px color-mix(in srgb, var(--ins-accent) 70%, transparent); }
.ins-brand-text { display: flex; flex-direction: column; line-height: 1.15; }
#ins-network-name { font-size: 12px; font-weight: bold; letter-spacing: 2px; color: var(--ins-accent); text-shadow: 0 0 6px color-mix(in srgb, var(--ins-accent) 45%, transparent); }
.ins-subtitle { font-size: 8px; letter-spacing: 2px; opacity: 0.45; margin-top: 3px; }
#ins-close { background: none; border: none; color: var(--text-dim, #7a8a83); font-size: 14px; cursor: pointer; padding: 0 2px; line-height: 1; font-family: inherit; }
#ins-close:hover { color: #ff4444; }

#ins-crt-bezel {
	position: relative;
	background: linear-gradient(160deg, #0a0806, #07050a);
	border-radius: 14px;
	padding: 16px;
	box-shadow: inset 0 2px 6px rgba(0,0,0,0.9), inset 0 0 0 1px rgba(255,255,255,0.03);
}
#ins-crt {
	position: relative;
	max-height: 70vh;
	overflow-y: auto;
	border-radius: 26px / 18px;
	background: radial-gradient(130% 130% at 50% 42%, color-mix(in srgb, var(--ins-accent) 9%, #0a0703) 55%, #060402 100%);
	box-shadow:
		inset 0 0 30px rgba(0,0,0,0.95),
		inset 0 0 6px color-mix(in srgb, var(--ins-accent) 30%, transparent),
		0 0 0 1px rgba(0,0,0,0.8);
}
#ins-crt-screen {
	position: relative;
	z-index: 2;
	padding: 14px 16px;
	color: var(--ins-accent);
	text-shadow: 0 0 5px color-mix(in srgb, var(--ins-accent) 55%, transparent);
}
.ins-crt-scanlines {
	position: absolute; inset: 0; z-index: 3; border-radius: inherit; pointer-events: none;
	background: repeating-linear-gradient(0deg, transparent 0 2px, rgba(0,0,0,0.16) 2px 3px);
}
.ins-crt-glass {
	position: absolute; inset: 0; z-index: 4; border-radius: inherit; pointer-events: none;
	background:
		linear-gradient(115deg, transparent 0 40%, rgba(255,240,220,0.10) 47%, rgba(255,240,220,0.03) 52%, transparent 60% 100%),
		radial-gradient(80% 55% at 26% 18%, rgba(255,255,255,0.12), transparent 60%),
		radial-gradient(120% 120% at 50% 50%, transparent 62%, rgba(0,0,0,0.55) 100%);
	box-shadow: inset 0 0 22px rgba(0,0,0,0.6);
}

.ins-top { display: flex; align-items: center; justify-content: space-between; font-size: 10px; letter-spacing: 1px; margin-bottom: 6px; }
.ins-title { font-weight: bold; letter-spacing: 2px; font-size: 11px; }
.ins-sub { font-size: 8px; letter-spacing: 1px; opacity: 0.85; }
.ins-wfstage { display: flex; justify-content: center; margin-bottom: 6px; }
#ins-wf { display: block; }
.ins-sel {
	border-top: 1px dashed color-mix(in srgb, var(--ins-accent) 30%, transparent);
	border-bottom: 1px dashed color-mix(in srgb, var(--ins-accent) 30%, transparent);
	padding: 8px 0; margin-bottom: 10px;
}
.ins-sel-name { font-size: 12px; margin-bottom: 3px; }
.ins-sel-status { font-size: 9px; letter-spacing: 1px; margin-bottom: 5px; }
.ins-sel-premium { font-size: 10px; letter-spacing: 1px; opacity: 0.8; margin-bottom: 8px; }
.ins-ok { color: var(--green); }
.ins-off { color: color-mix(in srgb, var(--ins-accent) 55%, transparent); }
.ins-section { font-size: 9px; letter-spacing: 3px; opacity: 0.6; margin: 8px 0 5px; }
.ins-list { display: flex; flex-direction: column; gap: 5px; margin-bottom: 4px; }
.ins-row {
	display: flex; align-items: center; justify-content: space-between; gap: 8px;
	background: color-mix(in srgb, var(--ins-accent) 6%, transparent);
	border: 1px solid color-mix(in srgb, var(--ins-accent) 25%, transparent);
	border-radius: 4px; color: var(--ins-accent); font-family: inherit; font-size: 10px;
	letter-spacing: 0.5px; padding: 6px 10px; cursor: pointer; text-align: left; text-shadow: inherit;
	transition: background 0.12s, border-color 0.12s, box-shadow 0.12s;
}
.ins-row:hover { background: color-mix(in srgb, var(--ins-accent) 14%, transparent); border-color: var(--ins-accent); }
.ins-row-sel { border-color: var(--ins-accent); box-shadow: 0 0 12px color-mix(in srgb, var(--ins-accent) 30%, transparent); }
.ins-row-name i { opacity: 0.7; font-style: normal; }
.ins-row-status { font-size: 8px; letter-spacing: 1px; white-space: nowrap; }
.ins-empty { font-size: 11px; opacity: 0.6; padding: 8px 0; }
.ins-claims { display: flex; flex-direction: column; gap: 6px; }
.ins-claim { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 10px; }
.ins-dim { opacity: 0.55; }
.ins-btn {
	background: linear-gradient(180deg, color-mix(in srgb, var(--ins-accent) 22%, transparent), color-mix(in srgb, var(--ins-accent) 8%, transparent));
	border: 1px solid var(--ins-accent); color: var(--ins-accent); font-family: inherit; font-size: 10px;
	letter-spacing: 1px; padding: 5px 10px; border-radius: 4px; cursor: pointer;
	box-shadow: inset 0 -2px 0 rgba(0,0,0,0.4); transition: filter 0.12s, box-shadow 0.12s;
}
.ins-btn:hover { filter: brightness(1.2); box-shadow: 0 0 12px color-mix(in srgb, var(--ins-accent) 40%, transparent); }
.ins-accent {}
.ins-warn { font-size: 9px; letter-spacing: 0.5px; color: var(--orange); margin-top: 8px; }
.ins-note { font-size: 9px; letter-spacing: 0.5px; opacity: 0.55; margin-top: 8px; line-height: 1.4; }
.atm-status-row {
	display: flex;
	align-items: center;
	gap: 10px;
	margin-bottom: 6px;
	font-size: 11px;
}
.atm-label {
	width: 54px;
	opacity: 0.55;
	letter-spacing: 1px;
	flex-shrink: 0;
}
.atm-value {
	font-size: 11px;
	letter-spacing: 1px;
}
.atm-bar-wrap {
	flex: 1;
	height: 8px;
	background: color-mix(in srgb, var(--atm-accent) 12%, #000);
	border: 1px solid color-mix(in srgb, var(--atm-accent) 30%, transparent);
	overflow: hidden;
	position: relative;
}
.atm-bar-fill {
	height: 100%;
	background: linear-gradient(90deg, color-mix(in srgb, var(--atm-accent) 60%, #000), var(--atm-accent));
	width: 100%;
	transition: width 0.4s;
	box-shadow: 0 0 10px color-mix(in srgb, var(--atm-accent) 70%, transparent);
}
/* Travelling gleam over the cash meter — reads as a live, charged reserve. */
.atm-bar-fill::after {
	content: '';
	position: absolute;
	top: 0; left: 0;
	width: 30%;
	height: 100%;
	background: linear-gradient(90deg, transparent, rgba(255,255,255,0.5), transparent);
	animation: atm-bar-gleam 2.6s linear infinite;
}
@keyframes atm-bar-gleam {
	0% { transform: translateX(-120%); }
	100% { transform: translateX(400%); }
}
.atm-bar-fill.atm-bar-low { background: linear-gradient(90deg, #7a1526, #ff3b5c); box-shadow: 0 0 10px rgba(255,59,92,0.6); }
/* .atm-divider is shared with the Media Deck panel below. */
.atm-divider {
	font-size: 9px;
	letter-spacing: 1px;
	opacity: 0.35;
	margin: 10px 0 8px;
}

/* ── Media Deck Panel — cyberpunk 8-track ── */
#mediadeck-panel {
	display: none;
	position: fixed;
	inset: 0;
	background: transparent;
	z-index: 200;
	align-items: center;
	justify-content: center;
}
#mediadeck-panel.active { display: flex; }
#mediadeck-box {
	background:
		repeating-linear-gradient(0deg, rgba(255,255,255,0.025) 0px, rgba(255,255,255,0.025) 1px, transparent 1px, transparent 3px),
		linear-gradient(155deg, #1a1024 0%, #100a1a 45%, #0b0712 100%);
	border: 2px solid #ff2fd6;
	border-radius: 6px;
	box-shadow:
		0 0 0 1px rgba(0, 240, 255, 0.35),
		0 0 28px rgba(255, 47, 214, 0.55),
		0 0 60px rgba(0, 240, 255, 0.25),
		inset 0 0 50px rgba(0, 0, 0, 0.6);
	font-family: 'Courier New', Courier, monospace;
	color: #18ffe0;
	min-width: 360px;
	max-width: 440px;
	width: 100%;
	max-height: 82vh;
	overflow-y: auto;
	padding: 24px 24px 20px;
	position: relative;
	clip-path: polygon(0 10px, 10px 0, calc(100% - 10px) 0, 100% 10px, 100% calc(100% - 10px), calc(100% - 10px) 100%, 10px 100%, 0 calc(100% - 10px));
}
.mediadeck-vents {
	position: absolute;
	top: 8px; left: 50%;
	transform: translateX(-50%);
	width: 70px; height: 5px;
	background: repeating-linear-gradient(90deg, rgba(0,240,255,0.6) 0 3px, transparent 3px 6px);
	opacity: 0.7;
}
#mediadeck-close {
	position: absolute;
	top: 9px;
	right: 12px;
	background: none;
	border: 1px solid rgba(255, 47, 214, 0.6);
	border-radius: 3px;
	color: #ff2fd6;
	font-size: 12px;
	line-height: 1;
	cursor: pointer;
	opacity: 0.85;
	padding: 2px 7px;
	font-family: inherit;
	text-shadow: 0 0 6px rgba(255, 47, 214, 0.9);
}
#mediadeck-close:hover { opacity: 1; background: rgba(255, 47, 214, 0.12); }
#mediadeck-header {
	text-align: center;
	margin-bottom: 16px;
	padding-bottom: 12px;
	border-bottom: 1px solid rgba(0, 240, 255, 0.35);
}
#mediadeck-header .atm-logo {
	color: #ff2fd6;
	text-shadow: 0 0 8px rgba(255, 47, 214, 0.8), 0 0 2px #fff;
}
#mediadeck-name {
	font-size: 18px;
	font-weight: bold;
	letter-spacing: 3px;
	color: #18ffe0;
	text-shadow: 0 0 8px rgba(24, 255, 224, 0.7);
}
#mediadeck-channel {
	margin-top: 4px;
	color: #b388ff;
	letter-spacing: 1px;
}
.mediadeck-cartridge-label {
	font-size: 9px;
	letter-spacing: 1px;
	color: #b388ff;
	opacity: 0.6;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	max-width: 90%;
}
.atm-status-row .atm-label {
	color: #00f0ff;
	letter-spacing: 2px;
	opacity: 0.8;
}
.mediadeck-light {
	display: inline-block;
	width: 10px;
	height: 10px;
	border-radius: 50%;
	background: #333;
	border: 1px solid rgba(255,255,255,0.15);
}
.mediadeck-light-green  { background: #18ffe0; box-shadow: 0 0 4px #18ffe0, 0 0 12px #18ffe0; animation: mediadeck-pulse 1.6s ease-in-out infinite; }
.mediadeck-light-orange { background: #ff9d2e; box-shadow: 0 0 4px #ff9d2e, 0 0 12px #ff9d2e; animation: mediadeck-pulse 1s ease-in-out infinite; }
.mediadeck-light-red    { background: #ff2f6e; box-shadow: 0 0 4px #ff2f6e, 0 0 10px #ff2f6e; }
/* ── Mediadeck broadcast preview ── */
#mediadeck-preview {
	margin: 8px 0 4px;
	border: 1px solid rgba(0, 240, 255, 0.2);
	border-radius: 2px;
	background:
		repeating-linear-gradient(0deg, transparent, transparent 3px, rgba(0,0,0,0.08) 3px, rgba(0,0,0,0.08) 4px),
		radial-gradient(ellipse at center, transparent 60%, rgba(0,0,0,0.4) 100%),
		#040c10;
	box-shadow: inset 0 0 18px rgba(0,0,0,0.5);
	overflow: hidden;
	position: relative;
}
#mediadeck-preview-header {
	font-size: 9px;
	font-family: var(--font-mono);
	letter-spacing: 2px;
	text-transform: uppercase;
	padding: 3px 8px;
	border-bottom: 1px solid rgba(0,240,255,0.12);
	text-align: right;
}
.mediadeck-preview-header-live     { color: #18ffe0; }
.mediadeck-preview-header-scripted { color: #ff9d2e; }
.mediadeck-preview-header-offline  { color: rgba(255,255,255,0.2); }
#mediadeck-preview-msgs {
	height: 100px;
	overflow-y: auto;
	padding: 6px 10px;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	scrollbar-width: none;
}
#mediadeck-preview-msgs::-webkit-scrollbar { display: none; }
/* Dead-air placeholder: when the linked channel has nothing on air at all (no
   live signal, no scheduled content, no tape), the little monitor shows a red
   [NO BROADCAST] card instead of a blank/stale screen — like a TV's NO SIGNAL. */
#mediadeck-no-broadcast {
	position: absolute;
	left: 0; right: 0; top: 23px; bottom: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	font-family: var(--font-mono);
	font-size: 12px;
	letter-spacing: 3px;
	color: #ff3b3b;
	text-shadow: 0 0 8px rgba(255, 59, 59, 0.6);
	background: rgba(4, 12, 16, 0.82);
	pointer-events: none;
	animation: mediadeck-nobroadcast-pulse 1.8s ease-in-out infinite;
}
#mediadeck-no-broadcast[hidden] { display: none; }
@keyframes mediadeck-nobroadcast-pulse {
	0%, 100% { opacity: 0.55; }
	50%      { opacity: 1; }
}
.mediadeck-preview-line {
	font-size: 10px;
	line-height: 1.4;
	color: #a0c8c0;
	font-family: var(--font-mono);
	word-break: break-word;
}
/* Styled preview lines mirror the TV panel, scaled into the small monitor. */
.mediadeck-preview-svg { text-align: center; }
.mediadeck-preview-svg svg { max-width: 100%; height: auto; }
.mediadeck-preview-ascii {
	margin: 0;
	font-size: 7px;
	line-height: 1.05;
	white-space: pre;
	color: #a0c8c0;
	font-family: var(--font-mono);
}
.mediadeck-preview-ticker { color: #ffc940; letter-spacing: 1px; }
@keyframes mediadeck-pulse {
	0%, 100% { opacity: 1; }
	50% { opacity: 0.4; }
}
/* On-screen overlays/graphics — the deck-preview counterpart to the TV's
   #tv-overlay-container, scaled down to sit over the small preview window. */
#mediadeck-overlay-container {
	position: absolute;
	left: 0;
	right: 0;
	top: 18px; /* below the preview header */
	bottom: 0;
	pointer-events: none;
	overflow: hidden;
	z-index: 5;
	display: flex;
	align-items: center;
	justify-content: center;
}
.mediadeck-overlay-text-card {
	text-align: center;
	background: rgba(0, 0, 0, 0.9);
	color: #e6f6f2;
	font-size: 10px;
	font-weight: 600;
	line-height: 1.4;
	letter-spacing: 0.04em;
	padding: 6px 10px;
	white-space: pre-wrap;
	border: 1px solid rgba(0, 240, 255, 0.5);
	max-width: 85%;
}
.mediadeck-overlay-lower-third {
	position: absolute;
	bottom: 4px;
	left: 0;
	right: 0;
	background: rgba(0, 0, 0, 0.9);
	border-top: 2px solid #18ffe0;
	padding: 3px 8px 4px;
}
.mediadeck-overlay-lt-name {
	font-size: 10px;
	font-weight: bold;
	color: #18ffe0;
	text-transform: uppercase;
	letter-spacing: 1px;
}
.mediadeck-overlay-lt-sub {
	font-size: 8px;
	color: #a0c8c0;
	letter-spacing: 0.5px;
	margin-top: 1px;
	opacity: 0.8;
}
.mediadeck-overlay-alert {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	background: rgba(160, 0, 0, 0.92);
	color: #fff;
	text-align: center;
	padding: 6px;
}
.mediadeck-overlay-alert-title {
	font-size: 12px;
	font-weight: bold;
	letter-spacing: 2px;
	text-transform: uppercase;
	line-height: 1.2;
}
.mediadeck-overlay-alert-sub {
	font-size: 9px;
	letter-spacing: 0.5px;
	margin-top: 3px;
	opacity: 0.85;
}
.atm-divider {
	color: #ff2fd6;
	opacity: 0.75;
	font-size: 10px;
	letter-spacing: 1px;
	margin: 12px 0 6px;
}
.mediadeck-list {
	max-height: 170px;
	overflow-y: auto;
	margin-bottom: 4px;
}
.mediadeck-empty {
	font-size: 11px;
	opacity: 0.45;
	padding: 8px 0;
	text-align: center;
	letter-spacing: 1px;
	color: #b388ff;
}
.mediadeck-cassette-row {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 6px 8px;
	background: linear-gradient(180deg, rgba(0,240,255,0.05), rgba(0,0,0,0.2));
	border: 1px solid rgba(0, 240, 255, 0.25);
	border-radius: 3px;
	margin-bottom: 5px;
	cursor: pointer;
	font-size: 11px;
	color: #18ffe0;
	transition: border-color 0.15s, background 0.15s;
}
.mediadeck-cassette-row:hover {
	border-color: #00f0ff;
	background: rgba(0, 240, 255, 0.1);
	box-shadow: 0 0 8px rgba(0, 240, 255, 0.3);
}
.mediadeck-cassette-row.active {
	border-color: #ff2fd6;
	color: #ff9ff0;
	background: linear-gradient(180deg, rgba(255,47,214,0.15), rgba(0,0,0,0.2));
	box-shadow: 0 0 10px rgba(255, 47, 214, 0.4);
}
.mediadeck-track-num {
	font-size: 9px;
	color: #b388ff;
	opacity: 0.7;
	flex-shrink: 0;
	width: 16px;
}
.mediadeck-cassette-spool {
	display: inline-block;
	width: 11px;
	height: 11px;
	flex-shrink: 0;
	border-radius: 50%;
	border: 2px solid currentColor;
	background:
		radial-gradient(circle at center, currentColor 1.5px, transparent 1.6px),
		conic-gradient(currentColor 0deg 8deg, transparent 8deg 90deg, currentColor 90deg 98deg, transparent 98deg 180deg, currentColor 180deg 188deg, transparent 188deg 270deg, currentColor 270deg 278deg, transparent 278deg 360deg);
	opacity: 0.85;
}
.mediadeck-cassette-spool.spinning { animation: mediadeck-spool-spin 1.4s linear infinite; }
@keyframes mediadeck-spool-spin { to { transform: rotate(360deg); } }
.mediadeck-cassette-name { flex: 1; letter-spacing: 0.5px; }
.mediadeck-cassette-cat { opacity: 0.55; font-size: 9px; text-transform: uppercase; letter-spacing: 1px; color: #b388ff; }
.mediadeck-playing-tag {
	font-size: 9px;
	color: #ff2fd6;
	letter-spacing: 1px;
	text-shadow: 0 0 6px rgba(255, 47, 214, 0.8);
	flex-shrink: 0;
}
.mediadeck-btn-row {
	display: flex;
	gap: 8px;
	margin-top: 10px;
}
.mediadeck-transport-btn {
	flex: 1;
	background: linear-gradient(180deg, rgba(0,240,255,0.18), rgba(0,240,255,0.05));
	border: 1px solid #00f0ff;
	border-radius: 3px;
	color: #9af6ff;
	font-family: inherit;
	font-size: 11px;
	letter-spacing: 2px;
	padding: 7px 0;
	cursor: pointer;
	text-shadow: 0 0 6px rgba(0, 240, 255, 0.6);
	transition: background 0.15s, box-shadow 0.15s, transform 0.08s;
}
.mediadeck-transport-btn:hover {
	background: rgba(0, 240, 255, 0.28);
	box-shadow: 0 0 12px rgba(0, 240, 255, 0.5);
}
.mediadeck-transport-btn:active { transform: translateY(1px); }
.mediadeck-eject-btn {
	background: linear-gradient(180deg, rgba(255,47,214,0.18), rgba(255,47,214,0.05));
	border-color: #ff2fd6;
	color: #ff9ff0;
	text-shadow: 0 0 6px rgba(255, 47, 214, 0.6);
}
.mediadeck-eject-btn:hover {
	background: rgba(255, 47, 214, 0.28);
	box-shadow: 0 0 12px rgba(255, 47, 214, 0.5);
}
.mediadeck-schedule-row {
	display: flex;
	gap: 10px;
	padding: 4px 6px;
	font-size: 11px;
	opacity: 0.85;
	border-bottom: 1px solid rgba(0, 240, 255, 0.1);
}
.mediadeck-schedule-time { opacity: 0.7; flex-shrink: 0; color: #00f0ff; }
.mediadeck-schedule-name { color: #b388ff; }

/* Media deck cassette window (spinning reels) */
#mediadeck-slot {
	position: relative;
	margin-bottom: 14px;
	border: 1px solid rgba(0, 240, 255, 0.2);
	border-radius: 6px;
	background: linear-gradient(180deg, #050208 0%, #0c0814 100%);
	box-shadow: inset 0 3px 8px rgba(0,0,0,0.85);
	display: flex;
	align-items: center;
	justify-content: space-between;
	/* Chunky VHS-style shell: tall vertical padding gives the cassette body real
	   video-cassette thickness (reels stay centred in the taller window). */
	padding: 50px 14px;
	overflow: visible;
	perspective: 380px;
}
#mediadeck-slot.loaded {
	border-color: rgba(255, 47, 214, 0.45);
	box-shadow: inset 0 3px 8px rgba(0,0,0,0.85), 0 0 14px rgba(255, 47, 214, 0.18);
}
#mediadeck-cartridge {
	width: 100%;
	height: 28px;
	border-radius: 2px;
	background: linear-gradient(180deg, #2a1638, #170a22);
	border: 1px solid rgba(255, 47, 214, 0.25);
	display: flex;
	align-items: center;
	justify-content: center;
	margin-bottom: 12px;
	transition: border-color 0.25s, box-shadow 0.25s;
}
#mediadeck-slot.loaded #mediadeck-cartridge {
	border-color: #ff2fd6;
	box-shadow: 0 0 10px rgba(255, 47, 214, 0.45), inset 0 0 14px rgba(255, 47, 214, 0.1);
}
#mediadeck-slot.loaded .mediadeck-cartridge-label {
	color: #ff9ff0;
	opacity: 1;
	text-shadow: 0 0 6px rgba(255, 47, 214, 0.7);
}
.mediadeck-cassette-body {
	position: absolute;
	inset: 4px;
	border-radius: 4px;
	/* Amber-brown broadcast cassette shell — clearly distinct from the
	   purple-black studio chassis. Bored spool-hole rims: each reel sits in a
	   socket whose upper lip catches light and lower lip falls into shadow. The
	   crescents are offset up/down from each reel centre so, with the Z-lift in
	   the drop animation below, the sockets read as real 3D bores as the tape
	   tilts in — not flat printed rings. (Reels cover the inner disc; only the
	   collar shows.) */
	background:
		radial-gradient(circle 30px at 32px calc(50% - 6px), rgba(255, 214, 150, 0.30) 55%, rgba(255, 214, 150, 0) 82%),
		radial-gradient(circle 30px at 32px calc(50% + 7px), rgba(0, 0, 0, 0.72) 52%, rgba(0, 0, 0, 0) 84%),
		radial-gradient(circle 30px at calc(100% - 32px) calc(50% - 6px), rgba(255, 214, 150, 0.30) 55%, rgba(255, 214, 150, 0) 82%),
		radial-gradient(circle 30px at calc(100% - 32px) calc(50% + 7px), rgba(0, 0, 0, 0.72) 52%, rgba(0, 0, 0, 0) 84%),
		radial-gradient(ellipse 26px 20px at 50% calc(50% - 5px), rgba(255, 214, 150, 0.24) 55%, rgba(255, 214, 150, 0) 82%),
		radial-gradient(ellipse 26px 20px at 50% calc(50% + 6px), rgba(0, 0, 0, 0.68) 52%, rgba(0, 0, 0, 0) 84%),
		linear-gradient(180deg, #221108 0%, #150b05 100%);
	border: 1px solid rgba(200, 130, 40, 0.45);
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 2;
	/* Chunky, thick-shell read: a top bevel highlight + deep bottom inner shade,
	   plus an outer drop shadow so the cassette floats above the deck floor and
	   reads as a solid 3D block rather than a flat sticker. */
	box-shadow:
		inset 0 2px 0 rgba(255, 210, 150, 0.10),
		inset 0 -3px 5px rgba(0,0,0,0.6),
		inset 0 0 12px rgba(0,0,0,0.7),
		0 5px 7px rgba(0,0,0,0.55),
		0 0 8px rgba(180, 110, 20, 0.14);
	opacity: 0;
	pointer-events: none;
	transition: opacity 0.22s ease-out;
}
#mediadeck-slot.loaded .mediadeck-cassette-body {
	opacity: 1;
	pointer-events: auto;
}
/* Cassette drop-in / pop-out choreography (see .inserting / .ejecting below) */
/* The tape tips forward (top toward viewer) as it comes down, then flattens as it
   slides home behind the glass — hinging on its bottom edge for the 3D lean. */
@keyframes mediadeck-cassette-drop {
	0%   { opacity: 0; transform: translateY(-150%) translateZ(32px) rotateX(-34deg); }
	12%  { opacity: 1; }
	58%  { transform: translateY(-6%) translateZ(28px) rotateX(-30deg); }
	76%  { transform: translateY(7%) translateZ(16px) rotateX(-7deg); }
	90%  { transform: translateY(-2%) translateZ(5px) rotateX(0deg); }
	100% { opacity: 1; transform: translateY(0) translateZ(0) rotateX(0deg); }
}
@keyframes mediadeck-cassette-rise {
	0%   { opacity: 1; transform: translateY(0) translateZ(0) rotateX(0deg); }
	25%  { transform: translateY(6%) translateZ(20px) rotateX(-10deg); }
	100% { opacity: 0; transform: translateY(-150%) translateZ(32px) rotateX(-34deg); }
}
#mediadeck-slot.inserting .mediadeck-cassette-body {
	opacity: 1;
	transform-origin: center bottom;
	animation: mediadeck-cassette-drop 0.8s cubic-bezier(.34,1.3,.5,1) both;
}
#mediadeck-slot.ejecting .mediadeck-cassette-body {
	opacity: 1;
	transform-origin: center bottom;
	animation: mediadeck-cassette-rise 0.6s cubic-bezier(.5,-0.2,.7,0.5) both;
}
#mediadeck-slot.inserting .mediadeck-empty-hint,
#mediadeck-slot.ejecting .mediadeck-empty-hint { opacity: 0; }

/* ── MicroReel ──────────────────────────────────────────────────────────────
   A surveillance clip loaded as a cassette is a MicroReel — a compact tape that
   seats inside a full cassette shell (VHS-C style). When one is loaded, the
   amber shell reads as an adapter bay: its full-size internals give way to a
   smaller nested cassette in SPECTER green. */
.mediadeck-mini-insert { display: none; }
#mediadeck-slot.mini.loaded .mediadeck-reel,
#mediadeck-slot.mini.loaded .mediadeck-tape-center,
#mediadeck-slot.mini.loaded .mediadeck-cassette-label-strip { opacity: 0 !important; }
#mediadeck-slot.mini.loaded .mediadeck-cassette-body {
	background: linear-gradient(180deg, #2a1710 0%, #170c06 100%);
	box-shadow:
		inset 0 2px 0 rgba(255, 210, 150, 0.10),
		inset 0 0 0 3px rgba(0, 0, 0, 0.4),
		inset 0 0 18px rgba(0, 0, 0, 0.78),
		0 5px 7px rgba(0, 0, 0, 0.55);
}
#mediadeck-slot.mini.loaded .mediadeck-mini-insert {
	display: flex; align-items: center; justify-content: center; gap: 8px;
	position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
	width: 60%; height: 58%; border-radius: 3px;
	background: linear-gradient(180deg, #0e2c25 0%, #071a15 100%);
	border: 1px solid rgba(57, 255, 158, 0.5);
	box-shadow:
		inset 0 2px 0 rgba(57, 255, 158, 0.16),
		inset 0 -3px 5px rgba(0, 0, 0, 0.6),
		0 3px 6px rgba(0, 0, 0, 0.55),
		0 0 9px rgba(57, 255, 158, 0.2);
	z-index: 5;
}
.mediadeck-mini-reel {
	width: 15px; height: 15px; border-radius: 50%;
	background: radial-gradient(circle at 50% 42%, #0a1512 42%, #05201a 60%);
	border: 2px solid rgba(57, 255, 158, 0.55);
	box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.8);
}
.mediadeck-mini-reel.spinning { animation: mediadeck-reel-spin 1.4s linear infinite; }
.mediadeck-mini-window {
	width: 22px; height: 9px; border-radius: 2px;
	background: repeating-linear-gradient(90deg, #10352b 0 2px, #0a241d 2px 4px);
	border: 1px solid rgba(57, 255, 158, 0.35);
}
.mediadeck-mini-label {
	position: absolute; bottom: 3px; left: 50%; transform: translateX(-50%);
	font-size: 7px; letter-spacing: 2px; color: rgba(57, 255, 158, 0.9);
	text-shadow: 0 0 4px rgba(57, 255, 158, 0.5); font-family: 'Courier New', monospace;
}
/* MicroReel badge on the cartridge label + library rows */
.mediadeck-micro-badge {
	display: inline-block; font-size: 8px; letter-spacing: 1px; padding: 0 3px;
	border: 1px solid rgba(57, 255, 158, 0.55); border-radius: 2px;
	color: #39ff9e; margin-right: 5px; vertical-align: middle;
}
/* Glass cover lid for mediadeck — hinge at bottom, top tilts toward viewer.
   deck-opening swings fully open; reels always visible through transparent glass. */
.mediadeck-glass-cover {
	position: absolute;
	inset: 4px;
	border-radius: 4px;
	/* Semi-transparent smoked glass with a diagonal sheen streak — reads as a
	   real pane while the reels stay visible spinning behind it. */
	background:
		linear-gradient(115deg,
			transparent 0%, transparent 38%,
			rgba(255,220,255,0.13) 47%, rgba(255,220,255,0.04) 52%,
			transparent 60%, transparent 100%),
		linear-gradient(170deg,
			rgba(255,100,255,0.08) 0%,
			rgba(200,80,255,0.03) 50%,
			rgba(160,60,255,0.06) 100%
		);
	border-top: 3px solid rgba(220, 180, 255, 0.55);
	border-left: 1px solid rgba(255, 47, 214, 0.10);
	border-right: 1px solid rgba(255, 47, 214, 0.10);
	border-bottom: 1px solid rgba(0, 0, 0, 0.25);
	box-shadow:
		inset 0 2px 0 rgba(255,255,255,0.13),
		inset 0 -1px 0 rgba(0,0,0,0.2),
		0 -2px 6px rgba(200, 140, 255, 0.12);
	transform-origin: bottom center;
	/* At rest the pane sits flush against the shell (no tilt); it only swings out
	   to show its 3D angle while the deck is opening/inserting/ejecting. */
	transform: rotateX(0deg);
	transition: transform 0.7s ease-in-out, border-top-width 0.7s ease-in-out;
	z-index: 5;
	pointer-events: none;
}
#mediadeck-slot.deck-opening .mediadeck-glass-cover,
#mediadeck-slot.inserting .mediadeck-glass-cover,
#mediadeck-slot.ejecting  .mediadeck-glass-cover {
	transform: rotateX(-78deg);
	border-top-width: 9px;
}
/* Empty slot indicator */
.mediadeck-empty-hint {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 8px;
	letter-spacing: 2.5px;
	color: rgba(0, 240, 255, 0.5);
	opacity: 0.55;
	z-index: 1;
	pointer-events: none;
	transition: opacity 0.2s;
}
#mediadeck-slot.loaded .mediadeck-empty-hint { opacity: 0; }
/* Reels always visible; brighten further when loaded */
.mediadeck-reel { opacity: 0.65; }
.mediadeck-tape-guide,
.mediadeck-tape-head { opacity: 0.4; }
#mediadeck-slot.loaded .mediadeck-reel { opacity: 1; }
/* Locked in: the capstan/guides rise up proud out of their recessed sockets,
   top-lit, as the tape seats; eject reverses it. */
#mediadeck-slot.loaded .mediadeck-tape-guide,
#mediadeck-slot.loaded .mediadeck-tape-head {
	opacity: 1;
	transform: translateY(-1px) scaleY(1);
	filter: brightness(1);
	/* Popped proud: real cast shadow BELOW the pin (it stands off the shell) +
	   a crisp top-lit highlight lip, so it reads as extended toward you. A faint
	   cyan glow keeps the deck's on-air identity. */
	box-shadow:
		0 3px 5px rgba(0,0,0,0.7),
		0 1px 1px rgba(0,0,0,0.5),
		inset 0 1.5px 0 rgba(255,255,255,0.55),
		inset 0 -2px 3px rgba(0,0,0,0.30),
		0 0 5px rgba(0, 240, 255, 0.35);
}
/* On eject the capstans sink back into their sockets FIRST (fast), so they're
   already recessed as the tape lifts away. Placed after the .loaded rule so it
   wins while .loaded is still set at the very start of the eject. */
#mediadeck-slot.ejecting .mediadeck-tape-guide,
#mediadeck-slot.ejecting .mediadeck-tape-head {
	transform: translateY(3px) scaleY(0.4);
	filter: brightness(0.55);
	box-shadow: inset 0 -2px 3px rgba(0, 0, 0, 0.85);
	transition: transform 0.16s ease-in, filter 0.16s ease-in, box-shadow 0.16s ease-in;
}
.mediadeck-cassette-label-strip {
	/* Pinned to the lower face of the shell so it clears the centred reels/head
	   and rides with the drop-in animation. */
	position: absolute;
	bottom: 5px;
	left: 50%;
	transform: translateX(-50%);
	z-index: 4;
	background: linear-gradient(135deg, #1a1408 0%, #0f0c04 100%);
	border: 1px solid rgba(220, 160, 60, 0.55);
	border-radius: 2px;
	padding: 3px 10px;
	font-size: 7px;
	color: #e8c47a;
	letter-spacing: 1.5px;
	text-transform: uppercase;
	max-width: 55%;
	text-align: center;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	text-shadow: 0 0 6px rgba(200, 140, 40, 0.8);
	box-shadow: 0 0 8px rgba(180, 120, 20, 0.25);
}
.mediadeck-reel {
	width: 44px;
	height: 44px;
	border-radius: 50%;
	border: 2px solid rgba(0, 240, 255, 0.3);
	background:
		radial-gradient(circle at center, rgba(5, 2, 8, 0.9) 20%, transparent 20%),
		conic-gradient(
			rgba(0, 240, 255, 0.18) 0deg 30deg, transparent 30deg 90deg,
			rgba(0, 240, 255, 0.18) 90deg 120deg, transparent 120deg 180deg,
			rgba(0, 240, 255, 0.18) 180deg 210deg, transparent 210deg 270deg,
			rgba(0, 240, 255, 0.18) 270deg 300deg, transparent 300deg 360deg
		),
		#0a0514;
	position: relative;
	/* sit above the cassette body so reels are always visible through the shell */
	z-index: 3;
	flex-shrink: 0;
	transition: border-color 0.3s, box-shadow 0.3s, opacity 0.3s;
}
.mediadeck-reel.spinning {
	animation: mediadeck-reel-spin 1.4s linear infinite;
	border-color: rgba(255, 47, 214, 0.7);
	box-shadow: 0 0 10px rgba(255, 47, 214, 0.4);
}
.mediadeck-reel-r.spinning { animation-direction: reverse; }
@keyframes mediadeck-reel-spin {
	from { transform: rotate(0deg); }
	to   { transform: rotate(360deg); }
}
.mediadeck-reel-hub {
	position: absolute;
	inset: 14px;
	border-radius: 50%;
	background: radial-gradient(circle at 40% 40%, rgba(0, 240, 255, 0.15), rgba(0, 240, 255, 0.03));
	border: 1px solid rgba(0, 240, 255, 0.25);
}
.mediadeck-tape-center {
	flex: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 5px;
	padding: 0 6px;
	position: relative;
	z-index: 3;
}
.mediadeck-tape-guide {
	width: 4px;
	height: 24px;
	/* Accent-coloured capstan at ALL times (not the deck's tinted fill) —
	   top-lit machined pin; recessed vs proud is shading + scale, not fill swap. */
	background: linear-gradient(to bottom, var(--accent) 0%, var(--accent) 42%, var(--accent-dim) 100%);
	border-radius: 2px;
	transition: opacity 0.3s;
}
.mediadeck-tape-head {
	width: 8px;
	height: 18px;
	background: linear-gradient(to bottom, var(--accent) 0%, var(--accent) 42%, var(--accent-dim) 100%);
	border-radius: 2px;
	border: 1px solid var(--accent-dim);
	transition: opacity 0.3s;
}
/* Capstan/head sockets: a dark recessed cutout punched through the shell behind
   each centre element, with an amber-rimmed edge matching the cassette border,
   so the guides/head read as slotting down into holes in the tape rather than
   floating on top of the shell. Positioned per-element so they self-align. */
.mediadeck-tape-guide,
.mediadeck-tape-head {
	position: relative;
	/* Recessed by default: the grey pin sits sunk flush in its socket — short
	   (grown from the base), dimmed into shadow, dark inner-bottom, NO cast shadow.
	   Origin bottom means it grows UPWARD out of the socket when a tape seats, so
	   .loaded reads as the pin extending toward you rather than lighting in place. */
	transform-origin: bottom center;
	transform: translateY(3px) scaleY(0.4);
	filter: brightness(0.55);
	box-shadow: inset 0 -2px 3px rgba(0, 0, 0, 0.85);
	transition: opacity 0.3s, transform 0.35s ease, box-shadow 0.35s ease, filter 0.35s ease;
}
.mediadeck-tape-guide::before,
.mediadeck-tape-head::before {
	content: '';
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	border-radius: 3px;
	background: #0c0603;
	box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.9), 0 0 0 1px rgba(200, 130, 40, 0.4);
	z-index: -1;
}
.mediadeck-tape-guide::before { width: 9px; height: 30px; }
.mediadeck-tape-head::before  { width: 15px; height: 24px; }
.mediadeck-load-picker {
	position: absolute;
	inset: 0;
	background: rgba(8, 6, 22, 0.96);
	border: 1px solid rgba(255, 47, 214, 0.5);
	display: flex;
	flex-direction: column;
	gap: 8px;
	padding: 16px 12px 12px;
	z-index: 10;
	border-radius: 4px;
}
.mediadeck-load-picker[hidden] { display: none; }
.mediadeck-load-picker-title {
	color: #ff2fd6;
	font-size: 10px;
	letter-spacing: 1.5px;
	text-align: center;
}
.mediadeck-load-picker-list {
	flex: 1;
	overflow-y: auto;
	display: flex;
	flex-direction: column;
	gap: 4px;
	min-height: 0;
	max-height: 220px;
}
.mediadeck-load-picker-item {
	padding: 6px 8px;
	font-size: 11px;
	border: 1px solid rgba(0, 240, 255, 0.2);
	border-radius: 2px;
	cursor: pointer;
	color: #b388ff;
	transition: background 0.1s, border-color 0.1s;
}
.mediadeck-load-picker-item:hover {
	background: rgba(0, 240, 255, 0.1);
	border-color: #00f0ff;
	color: #00f0ff;
}
.mediadeck-load-picker-empty {
	font-size: 11px;
	color: var(--text-dim);
	text-align: center;
	padding: 16px 0;
}

/* ── TV Panel ── */
#tv-panel {
	display: none;
	position: fixed;
	inset: 0;
	background: transparent;
	z-index: 200;
	align-items: center;
	justify-content: center;
	pointer-events: none;
}
#tv-panel.active {
	display: flex;
}
#tv-panel.active #tv-window {
	pointer-events: all;
}
#tv-window {
	/* Themeable CSS variables — overridden by applyTvTheme() */
	--tv-bg: #080c10;
	--tv-border: rgba(0, 220, 180, 0.45);
	--tv-text: #b8d4c8;
	--tv-header-color: #00dbb4;
	--tv-live-color: #ff4455;
	--tv-ticker-color: #ffc940;

	display: flex;
	flex-direction: column;
	width: min(760px, 96vw, calc(90vh * 760 / 520));
	aspect-ratio: 760 / 520;
	background: var(--tv-bg);
	border: 3px solid #1a1a1a;
	outline: 1px solid var(--tv-border);
	box-shadow:
		0 0 0 6px #111,
		0 0 0 7px rgba(0, 220, 180, 0.08),
		0 0 60px rgba(0, 180, 140, 0.22),
		0 8px 40px rgba(0, 0, 0, 0.8),
		inset 0 0 80px rgba(0, 0, 0, 0.5);
	font-family: var(--font-mono);
	color: var(--tv-text);
	position: relative;
	border-radius: 4px;
	cursor: default;
	user-select: none;
	/* Scanline overlay + CRT vignette */
	background-image:
		repeating-linear-gradient(
			0deg,
			transparent,
			transparent 3px,
			rgba(0, 0, 0, 0.09) 3px,
			rgba(0, 0, 0, 0.09) 4px
		),
		radial-gradient(ellipse at center, transparent 55%, rgba(0,0,0,0.55) 100%);
}
/* CRT phosphor theme */
#tv-window[data-theme="crt"] {
	--tv-bg: #020c02;
	--tv-border: rgba(0, 255, 60, 0.45);
	--tv-text: #00dd33;
	--tv-header-color: #00ff44;
	--tv-live-color: #ffee00;
	--tv-ticker-color: #00ff44;
	background-image: repeating-linear-gradient(
		0deg, transparent, transparent 2px,
		rgba(0, 0, 0, 0.18) 2px, rgba(0, 0, 0, 0.18) 3px
	);
}
/* Emergency broadcast theme */
#tv-window[data-theme="emergency"] {
	--tv-bg: #0a0000;
	--tv-border: rgba(255, 30, 0, 0.7);
	--tv-text: #ff9988;
	--tv-header-color: #ff2200;
	--tv-live-color: #ffffff;
	--tv-ticker-color: #ff4400;
}
/* Security monitor theme */
#tv-window[data-theme="security"] {
	--tv-bg: #000a08;
	--tv-border: rgba(0, 200, 100, 0.4);
	--tv-text: #a0d0b0;
	--tv-header-color: #00cc77;
	--tv-live-color: #ff4422;
	--tv-ticker-color: #00cc77;
}
/* Pirate TV theme */
#tv-window[data-theme="pirate"] {
	--tv-bg: #0a0808;
	--tv-border: rgba(220, 160, 0, 0.5);
	--tv-text: #ddc0a0;
	--tv-header-color: #ff9900;
	--tv-live-color: #ff4444;
	--tv-ticker-color: #ff9900;
}

/* ── TV chassis skins (data-skin) ──────────────────────────────────────────
   A skin is the physical set: bezel, corners, surface texture. It's a property
   of the device (furniture flag `tv_skin`), orthogonal to the per-channel colour
   `data-theme`. The default set is the CRT chassis styled on #tv-window above;
   skins below re-shape that chassis. Placed after the themes so the chassis
   overrides (background-image, box-shadow) win on selector-specificity ties. */
#tv-window[data-skin="flatscreen"] {
	/* High-gloss piano-black set seated in a machined-chrome trim ring — flat
	   glass, no CRT scanlines/vignette. The layered box-shadow builds the metal
	   ring: a bright top-lit edge grading down to a shadowed underside so the
	   chrome reads as a real bevel catching light from above, then the whole set
	   floats on a deep contact shadow. */
	border: 2px solid #23262b;
	border-radius: 12px;
	outline: none;
	box-shadow:
		/* machined-chrome trim — directional so the top edge is lit, base shadowed */
		0 1px 0 3px #f4f7fa,
		0 0 0 4px #ccd3da,
		0 0 0 6px #8b9199,
		0 2px 0 6px #565b62,
		0 0 0 9px #2a2d32,
		0 0 0 12px #0c0d0f,
		/* theme-colored ambient bias-light — a soft backlit halo that picks up the
		   channel's accent, like the glow behind a premium set */
		0 0 52px 1px color-mix(in srgb, var(--tv-header-color) 24%, transparent),
		/* the set floating in the room */
		0 28px 72px -14px rgba(0, 0, 0, 0.88),
		0 10px 26px rgba(0, 0, 0, 0.6),
		/* glass seated into the bezel — bright hairline lip + inner shade */
		inset 0 0 0 1px rgba(255, 255, 255, 0.07),
		inset 0 2px 20px rgba(0, 0, 0, 0.72),
		inset 0 0 75px rgba(0, 0, 0, 0.5);
	background-image:
		linear-gradient(180deg, rgba(255, 255, 255, 0.05), transparent 10%),
		radial-gradient(ellipse at 50% 42%, transparent 60%, rgba(0, 0, 0, 0.55) 100%);
}
/* Chrome bezel catches a cool specular highlight along the top rails */
#tv-window[data-skin="flatscreen"] #tv-header {
	background: linear-gradient(180deg, rgba(226, 235, 244, 0.15), rgba(255, 255, 255, 0.02) 42%, rgba(0, 0, 0, 0.4));
	box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.13);
}
#tv-window[data-skin="flatscreen"] #tv-footer {
	background: linear-gradient(0deg, rgba(226, 235, 244, 0.07), rgba(0, 0, 0, 0.5));
	box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.09);
}
/* Ceiling-light strip — a soft reflected light bar across the top of the glass,
   the hallmark of a glossy flatscreen. Kept short and low-opacity so it reads as
   a reflection without hurting the readability of the top text lines. */
#tv-window[data-skin="flatscreen"]::before {
	content: "";
	position: absolute;
	top: 45px;
	left: 3px;
	right: 3px;
	height: 78px;
	pointer-events: none;
	z-index: 41;
	border-radius: 3px 3px 50% 50% / 3px 3px 34px 34px;
	background: linear-gradient(180deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.025) 45%, transparent 100%);
	mix-blend-mode: screen;
}
/* Glass reflection — layered specular over the screen glass, not just the bezel
   shell. A crisp diagonal light streak slowly drifts across the surface (living
   glass), over a soft top-left ambient glare. Screen-blended so it only ever
   brightens; the corner darkening lives on the window background above. Scoped to
   the screen area (clear of the ~45px header and 72px footer) so text stays
   legible. pointer-events:none; z-index sits above content/static but below the
   scorebug (45) and overlays (50) so on-screen graphics aren't washed out. */
#tv-window[data-skin="flatscreen"]::after {
	content: "";
	position: absolute;
	top: 45px;
	bottom: 72px;
	left: 3px;
	right: 3px;
	pointer-events: none;
	z-index: 40;
	border-radius: 3px;
	background:
		linear-gradient(118deg, transparent 32%, rgba(255, 255, 255, 0.06) 45%, rgba(255, 255, 255, 0.16) 50%, rgba(255, 255, 255, 0.05) 55%, transparent 68%),
		radial-gradient(120% 85% at 12% 0%, rgba(205, 224, 238, 0.11), transparent 55%);
	background-size: 300% 100%, 100% 100%;
	background-position: -40% 0, 0 0;
	background-repeat: no-repeat;
	mix-blend-mode: screen;
	animation: tv-glass-drift 15s ease-in-out infinite alternate;
}
@keyframes tv-glass-drift {
	from { background-position: -40% 0, 0 0; }
	to   { background-position: 140% 0, 0 0; }
}
@media (prefers-reduced-motion: reduce) {
	#tv-window[data-skin="flatscreen"]::after { animation: none; background-position: 40% 0, 0 0; }
}
/* Station theme cross-fade — every themed element eases between colours when the
   channel (and its theme) changes. The colour vars live on #tv-window; children
   read them, so the transition sits on each element's concrete colour props.
   applyTvTheme() routes changes through the neutral default theme, so tuning
   between stations fades out to a default look and back in like a real dial. */
#tv-window,
#tv-header, #tv-footer, #tv-content,
#tv-station-name, #tv-channel-num, #tv-program-name,
#tv-messages .tv-msg, #tv-ticker-inner, #tv-freq-display,
#tv-knob-wrap, .tv-knob-ring, .tv-knob-line,
.tv-overlay-lower-third, .tv-overlay-lt-name, .tv-overlay-lt-sub,
#tv-static {
	transition: background-color 0.5s ease, border-color 0.5s ease,
		outline-color 0.5s ease, color 0.5s ease, fill 0.5s ease, stroke 0.5s ease;
}

/* Header */
#tv-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 8px 14px;
	border-bottom: 1px solid var(--tv-border);
	flex-shrink: 0;
	background: rgba(0, 40, 30, 0.5);
	gap: 10px;
	position: relative;
	z-index: 30;
}
#tv-header-left,
#tv-header-center,
#tv-header-right {
	display: flex;
	align-items: center;
	gap: 10px;
}
#tv-header-center {
	flex: 1;
	justify-content: center;
}
#tv-station-name {
	font-size: 12px;
	font-weight: bold;
	letter-spacing: 2px;
	color: var(--tv-header-color);
	text-transform: uppercase;
}
#tv-channel-num {
	font-size: 11px;
	color: rgba(0, 219, 180, 0.6);
	letter-spacing: 1px;
}
#tv-program-name {
	font-size: 11px;
	color: rgba(184, 212, 200, 0.55);
	font-style: italic;
}
#tv-live-badge {
	display: flex;
	align-items: center;
	gap: 5px;
	font-size: 10px;
	letter-spacing: 2px;
	color: var(--tv-live-color);
	text-transform: uppercase;
	font-weight: bold;
	transition: opacity 0.3s;
}
#tv-live-badge.tv-scrolled {
	opacity: 0.35;
}
.tv-live-dot {
	animation: tv-live-pulse 1.4s ease-in-out infinite;
	color: #ff3344;
}
@keyframes tv-live-pulse {
	0%, 100% { opacity: 1; }
	50% { opacity: 0.15; }
}
#tv-close-btn {
	background: #0c100e;
	border: 2px solid #1e2e28;
	color: #2e4840;
	font-family: inherit;
	font-size: 15px;
	line-height: 1;
	cursor: pointer;
	padding: 0;
	border-radius: 50%;
	width: 28px;
	height: 28px;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: color 0.2s, border-color 0.2s, box-shadow 0.2s, background 0.2s;
}
#tv-close-btn:hover {
	background: #180808;
	color: #cc2000;
	border-color: #551000;
	box-shadow: 0 0 10px rgba(180, 20, 0, 0.45), inset 0 0 5px rgba(180, 20, 0, 0.15);
}
/* Read-aloud toggle — matches the close button; lights up when engaged */
#tv-read-btn {
	background: #0c100e;
	border: 2px solid #1e2e28;
	color: #2e4840;
	font-family: inherit;
	font-size: 13px;
	line-height: 1;
	cursor: pointer;
	padding: 0;
	border-radius: 50%;
	width: 28px;
	height: 28px;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: color 0.2s, border-color 0.2s, box-shadow 0.2s, background 0.2s;
}
#tv-read-btn:hover {
	color: var(--tv-header-color, #37d67a);
	border-color: var(--tv-header-color, #2e6a44);
}
#tv-read-btn.on {
	color: var(--tv-header-color, #37d67a);
	border-color: var(--tv-header-color, #37d67a);
	box-shadow: 0 0 10px rgba(55, 214, 122, 0.4), inset 0 0 5px rgba(55, 214, 122, 0.15);
}
/* TV-guide toggle — same chassis button as read-aloud; always available, lit while
   the schedule sub-screen is open (.on). */
#tv-schedule-btn {
	background: #0c100e;
	border: 2px solid #1e2e28;
	color: #2e4840;
	font-family: inherit;
	font-size: 13px;
	line-height: 1;
	cursor: pointer;
	padding: 0;
	border-radius: 50%;
	width: 28px;
	height: 28px;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: color 0.2s, border-color 0.2s, box-shadow 0.2s, background 0.2s;
}
#tv-schedule-btn:hover {
	color: var(--tv-header-color, #37d67a);
	border-color: var(--tv-header-color, #2e6a44);
}
#tv-schedule-btn.on {
	color: var(--tv-header-color, #37d67a);
	border-color: var(--tv-header-color, #37d67a);
	box-shadow: 0 0 10px rgba(55, 214, 122, 0.4), inset 0 0 5px rgba(55, 214, 122, 0.15);
}
/* Gameday toggle — same chassis button as read-aloud; hidden until a sports
   broadcast reveals it (.avail), lit while the sub-screen is open (.on). */
#tv-gameday-btn {
	display: none;
	background: #0c100e;
	border: 2px solid #1e2e28;
	color: #2e4840;
	font-family: inherit;
	font-size: 14px;
	line-height: 1;
	cursor: pointer;
	padding: 0;
	border-radius: 50%;
	width: 28px;
	height: 28px;
	align-items: center;
	justify-content: center;
	transition: color 0.2s, border-color 0.2s, box-shadow 0.2s, background 0.2s;
}
#tv-gameday-btn.avail { display: flex; }
#tv-gameday-btn:hover {
	color: var(--tv-header-color, #37d67a);
	border-color: var(--tv-header-color, #2e6a44);
}
#tv-gameday-btn.on {
	color: var(--tv-header-color, #37d67a);
	border-color: var(--tv-header-color, #37d67a);
	box-shadow: 0 0 10px rgba(55, 214, 122, 0.4), inset 0 0 5px rgba(55, 214, 122, 0.15);
}
/* Content area */
#tv-content {
	flex: 1;
	overflow: hidden;
	padding: 14px 18px;
	position: relative;
}
#tv-messages {
	display: flex;
	flex-direction: column;
	gap: 5px;
}
.tv-msg {
	font-size: 13px;
	line-height: 1.5;
	color: var(--tv-text);
}
.tv-msg-raw { color: var(--tv-text); }
.tv-msg-ticker { color: var(--tv-ticker-color); font-style: italic; }
.tv-msg-ambient { color: var(--tv-text); opacity: 0.6; font-style: italic; }
.tv-msg-stage_direction { color: var(--tv-header-color); opacity: 0.55; font-size: 0.88em; }
/* The renderer emits the style key verbatim (`tv-msg-ascii_art`); the hyphen spelling
   alone never matched, so none of this landed — the auto-fit's line-height assumption
   included. Both spellings are matched so the rule applies either way. */
.tv-msg-ascii-art,
.tv-msg-ascii_art {
	font-size: 12px;
	line-height: 1.3;
	color: var(--tv-header-color);
	white-space: pre;
	margin: 6px 0;
	text-align: center;
}
.tv-msg-svg {
	display: block;
	width: 100%;
	padding: 4px 0;
	line-height: 0;
}
.tv-msg-svg svg {
	display: block;
	width: 100%;
	height: auto;
	border-radius: 2px;
}
/* Credits scroll */
.tv-credits-wrap {
	position: absolute;
	inset: 0;
	overflow: hidden;
}
.tv-credits-inner {
	position: absolute;
	left: 0; right: 0;
	text-align: center;
	padding: 0 24px;
	will-change: transform;
}
.tv-credits-inner .credits-header {
	color: var(--tv-header-color);
	font-size: 1.05em;
	letter-spacing: 0.08em;
	margin: 1.4em 0 0.3em;
	font-weight: bold;
}
.tv-credits-inner .credits-line {
	color: var(--tv-text);
	font-size: 12px;
	line-height: 1.7;
	margin: 0;
}
.tv-credits-inner .credits-blank { height: 0.9em; }
/* Footer ticker */
#tv-footer {
	flex-shrink: 0;
	border-top: 1px solid var(--tv-border);
	background: rgba(0, 20, 15, 0.7);
	height: 72px;
	display: flex;
	align-items: center;
	overflow: visible;
	position: relative;
	z-index: 30;
}
#tv-ticker-track {
	width: 100%;
	overflow: hidden;
	white-space: nowrap;
}
#tv-ticker-inner {
	display: inline-block;
	white-space: nowrap;
	font-size: 11px;
	color: var(--tv-ticker-color);
	letter-spacing: 0.5px;
	will-change: transform;
}
/* Off-air static loop */
.tv-static-loop {
	animation: tv-static-flicker 0.12s steps(1) infinite;
}
@keyframes tv-static-flicker {
	0%, 100% { opacity: 0.9; }
	50%       { opacity: 0.6; }
}
/* Frequency tuner in TV footer */
#tv-tuner-wrap {
	display: flex;
	align-items: center;
	gap: 6px;
	padding: 0 6px;
}
#tv-freq-display {
	font-size: 11px;
	color: var(--tv-header-color);
	min-width: 28px;
	text-align: right;
	font-variant-numeric: tabular-nums;
	letter-spacing: 0.5px;
}
/* Passive ambient TV message in main chat */
.broadcast-ambient {
	color: var(--text-dim);
	font-size: 11px;
	font-style: italic;
	opacity: 0.6;
}
/* Overlay container — sits above content, below header */
#tv-overlay-container {
	position: absolute;
	inset: 0;
	pointer-events: none;
	overflow: hidden;
	z-index: 50;
	display: flex;
	align-items: center;
	justify-content: center;
	/* Offset centering into the content area, clear of header (~45px) and footer (72px) */
	padding-top: 45px;
	padding-bottom: 172px;
}

/* ── Sports score-bug (persistent overlay) ─────────────────────────────────────
   Bottom-right "bug" that stays on-screen for the whole game and updates in place.
   Anchored below the play-by-play text (which fills from the top) so it never
   blocks a line. Sport-agnostic: two team/score rows + a status line, always.
   Baseball adds the base diamond + out dots when the payload carries them; other
   sports omit those and the bug shows just scores + status. Themed off the TV vars. */
#tv-scorebug {
	position: absolute;
	bottom: 82px;
	right: 10px;
	z-index: 45;
	display: none;
	pointer-events: none;
	background: rgba(0, 0, 0, 0.82);
	border: 1px solid var(--tv-border);
	border-left: 3px solid var(--tv-header-color);
	border-radius: 2px;
	overflow: hidden;
	box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
	animation: tv-sb-in 0.3s ease-out;
}
#tv-scorebug.on { display: flex; }
.tv-sb-scores { display: flex; flex-direction: column; min-width: 80px; }
.tv-sb-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 3px 9px;
	font-size: 13px;
	line-height: 1.25;
	color: rgba(220, 240, 235, 0.8);
}
.tv-sb-row:first-child { border-bottom: 1px solid rgba(255, 255, 255, 0.08); }
.tv-sb-row.lead { color: #fff; }
.tv-sb-row.lead .tv-sb-num { color: var(--tv-header-color); }
.tv-sb-team { font-weight: bold; letter-spacing: 1px; }
.tv-sb-num { font-weight: bold; font-variant-numeric: tabular-nums; min-width: 16px; text-align: right; }
.tv-sb-state {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 3px;
	padding: 4px 9px;
	background: rgba(255, 255, 255, 0.04);
	border-left: 1px solid rgba(255, 255, 255, 0.08);
}
.tv-sb-diamond { width: 26px; height: 26px; display: block; }
.tv-sb-base { fill: rgba(255, 255, 255, 0.12); stroke: rgba(255, 255, 255, 0.35); stroke-width: 1; }
.tv-sb-base.on { fill: var(--tv-header-color); stroke: var(--tv-header-color); }
.tv-sb-status {
	font-size: 9px;
	letter-spacing: 1px;
	color: var(--tv-header-color);
	text-transform: uppercase;
	white-space: nowrap;
}
.tv-sb-outs { display: flex; gap: 3px; }
.tv-sb-out {
	width: 5px; height: 5px; border-radius: 50%;
	background: rgba(255, 255, 255, 0.18);
	border: 1px solid rgba(255, 255, 255, 0.3);
}
.tv-sb-out.on { background: #ff3344; border-color: #ff3344; }
@keyframes tv-sb-in {
	from { opacity: 0; transform: translateY(6px); }
	to   { opacity: 1; transform: translateY(0); }
}

/* ── Gameday sub-screen ─────────────────────────────────────────────────────────
   The animated at-bat view (client/game/js/panels/gameday.js). Covers the content
   area (clear of the header/footer) when the player toggles it on; renders the same
   structured data Chip is narrating as a live diamond, pitch plot, matchup + play
   card, and a running play-by-play feed. Themed off the TV chassis variables. */
#tv-gameday {
	position: absolute;
	inset: 45px 0 72px 0;
	z-index: 48;
	display: none;
	flex-direction: column;
	background:
		repeating-linear-gradient(0deg, transparent 0 2px, rgba(0,0,0,0.10) 2px 3px),
		radial-gradient(ellipse at 50% -10%, rgba(0, 60, 45, 0.35), transparent 60%),
		var(--tv-bg, #080c10);
	color: var(--tv-text, #b8d4c8);
	font-family: var(--font-mono);
	padding: 7px 9px 6px;
	gap: 6px;
	overflow: hidden;
	animation: gd-in 0.25s ease-out;
}
#tv-gameday.on { display: flex; }
@keyframes gd-in { from { opacity: 0; } to { opacity: 1; } }

/* ── TV guide sub-screen ─────────────────────────────────────────────────────── */
/* Standings toggle — same chassis button as the TV guide, but hidden until a sports
   broadcast's first score-bug reveals it (.avail), lit while the panel is open (.on). */
#tv-standings-btn {
	background: #0c100e;
	border: 2px solid #1e2e28;
	color: #2e4840;
	font-family: inherit;
	font-size: 13px;
	line-height: 1;
	cursor: pointer;
	padding: 0;
	border-radius: 50%;
	width: 28px;
	height: 28px;
	display: none;
	align-items: center;
	justify-content: center;
	transition: color 0.2s, border-color 0.2s, box-shadow 0.2s, background 0.2s;
}
#tv-standings-btn.avail { display: flex; }
#tv-standings-btn:hover {
	color: var(--tv-header-color, #37d67a);
	border-color: var(--tv-header-color, #2e6a44);
}
#tv-standings-btn.on {
	color: var(--tv-header-color, #37d67a);
	border-color: var(--tv-header-color, #37d67a);
	box-shadow: 0 0 10px rgba(55, 214, 122, 0.4), inset 0 0 5px rgba(55, 214, 122, 0.15);
}

/* Standings PANEL — the on-demand league table. Its own overlay (same placement as
   the TV guide) rather than the corner bug, so the player-held table never fights the
   server-thrown transient one for the same corner. */
#tv-standings-panel {
	position: absolute;
	inset: 45px 0 72px 0;
	z-index: 48;
	display: none;
	flex-direction: column;
	background:
		repeating-linear-gradient(0deg, transparent 0 2px, rgba(0,0,0,0.10) 2px 3px),
		radial-gradient(ellipse at 50% -10%, rgba(0, 60, 45, 0.35), transparent 60%),
		var(--tv-bg, #080c10);
	color: var(--tv-text, #b8d4c8);
	font-family: var(--font-mono);
	padding: 8px 10px;
	overflow: hidden;
	animation: gd-in 0.25s ease-out;
}
#tv-standings-panel.on { display: flex; }

/* Standings-panel rows. Bare classes (no id scope) so the Tablet TV app's viewport
   gets the identical table — same rule the rest of the sports layers follow. */
.tv-stp-list { overflow-y: auto; flex: 1; min-height: 0; }
.tv-stp-row {
	display: flex; align-items: center; gap: 8px;
	padding: 3px 4px; font-size: 11px;
	border-bottom: 1px solid rgba(255,255,255,0.05);
}
.tv-stp-row.lead { color: var(--tv-header-color, #37d67a); font-weight: bold; }
.tv-stp-hdr {
	font-size: 9px; letter-spacing: 1px; opacity: 0.65;
	border-bottom: 1px solid var(--tv-border, #1e2e28); margin-bottom: 2px;
}
.tv-stp-rank { width: 1.6em; text-align: right; opacity: 0.7; flex: none; }
.tv-stp-team { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tv-stp-rec  { width: 4.2em; text-align: right; flex: none; font-variant-numeric: tabular-nums; }
.tv-stp-pct  { width: 3.2em; text-align: right; flex: none; opacity: 0.8; font-variant-numeric: tabular-nums; }
.tv-stp-rd   { width: 3.2em; text-align: right; flex: none; opacity: 0.8; font-variant-numeric: tabular-nums; }

#tv-schedule {
	position: absolute;
	inset: 45px 0 72px 0;
	z-index: 48;
	display: none;
	flex-direction: column;
	background:
		repeating-linear-gradient(0deg, transparent 0 2px, rgba(0,0,0,0.10) 2px 3px),
		radial-gradient(ellipse at 50% -10%, rgba(0, 60, 45, 0.35), transparent 60%),
		var(--tv-bg, #080c10);
	color: var(--tv-text, #b8d4c8);
	font-family: var(--font-mono);
	padding: 8px 10px;
	overflow: hidden;
	animation: gd-in 0.25s ease-out;
}
#tv-schedule.on { display: flex; }
.tv-sched-head {
	display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
	padding-bottom: 6px; margin-bottom: 4px;
	border-bottom: 1px solid var(--tv-border, rgba(0,220,180,0.3));
	flex: 0 0 auto;
}
.tv-sched-title { font-weight: bold; letter-spacing: 1px; color: var(--tv-header-color, #37d67a); font-size: 13px; text-transform: uppercase; }
.tv-sched-clock { font-size: 13px; font-variant-numeric: tabular-nums; color: var(--tv-text, #b8d4c8); white-space: nowrap; }
.tv-sched-list { flex: 1 1 auto; overflow-y: auto; }
.tv-sched-row {
	display: flex; align-items: baseline; gap: 10px;
	padding: 4px 4px; font-size: 12.5px; line-height: 1.3;
	border-bottom: 1px solid rgba(255,255,255,0.05);
}
.tv-sched-when {
	flex: 0 0 84px; font-variant-numeric: tabular-nums;
	color: rgba(220, 240, 235, 0.6); text-transform: uppercase; letter-spacing: 0.5px;
}
.tv-sched-name { flex: 1 1 auto; color: var(--tv-text, #b8d4c8); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tv-sched-dur { flex: 0 0 auto; color: rgba(220, 240, 235, 0.4); font-size: 11px; white-space: nowrap; }
.tv-sched-row.now { background: rgba(0, 220, 180, 0.1); }
.tv-sched-row.now .tv-sched-when { color: var(--tv-header-color, #37d67a); font-weight: bold; }
.tv-sched-row.now .tv-sched-name { color: #fff; font-weight: bold; }
.tv-sched-empty { padding: 20px 8px; text-align: center; color: rgba(220, 240, 235, 0.5); font-size: 12.5px; }
.tv-sched-foot { flex: 0 0 auto; padding-top: 5px; font-size: 10.5px; color: rgba(220, 240, 235, 0.35); text-align: center; }

/* ── Score bug (broadcast strip) ─────────────────────────────────────────────── */
.gd-top { display: flex; align-items: stretch; justify-content: space-between; gap: 6px; height: 40px; flex: 0 0 auto; }
.gd-team {
	flex: 0 0 auto; display: flex; align-items: center; gap: 10px; padding: 0 14px; min-width: 104px;
	background: rgba(0, 0, 0, 0.5); border: 1px solid var(--tv-border, rgba(0,220,180,0.3)); border-radius: 3px;
}
.gd-team.home { justify-content: flex-end; }
.gd-team.lead { border-color: var(--tv-header-color, #37d67a); box-shadow: inset 0 0 12px rgba(0,220,180,0.12); }
.gd-abbr { font-weight: bold; letter-spacing: 2px; font-size: 16px; color: rgba(220, 240, 235, 0.7); }
.gd-team.lead .gd-abbr { color: #fff; }
.gd-score { font-weight: bold; font-size: 24px; font-variant-numeric: tabular-nums; color: var(--tv-text); }
.gd-team.lead .gd-score { color: var(--tv-header-color, #37d67a); }
.gd-mid {
	flex: 1 1 auto; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
	background: rgba(0, 0, 0, 0.35); border: 1px solid var(--tv-border, rgba(0,220,180,0.25)); border-radius: 3px;
}
.gd-inn { font-size: 11px; font-weight: bold; letter-spacing: 2px; color: var(--tv-header-color, #37d67a); text-transform: uppercase; }
.gd-mid-line { display: flex; align-items: center; gap: 12px; }
.gd-outs { display: flex; align-items: center; gap: 4px; }
.gd-out { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,0.14); border: 1px solid rgba(255,255,255,0.3); }
.gd-out.on { background: #ff3344; border-color: #ff3344; box-shadow: 0 0 5px rgba(255,51,68,0.6); }
.gd-count { font-size: 15px; font-weight: bold; font-variant-numeric: tabular-nums; letter-spacing: 1px; color: #fff; }

/* ── Main row: field (hero) + instrument rail ────────────────────────────────── */
.gd-main { flex: 1 1 auto; display: flex; gap: 8px; min-height: 0; }
.gd-fieldwrap { flex: 1 1 auto; display: flex; align-items: center; justify-content: center; min-width: 0; }

/* Top-down field. Box a touch wider than tall so the extended outfield fits with the
   fielders on grass; sized to the available height, centred, letterboxed by the wrap. */
.gd-field {
	position: relative; height: 100%; aspect-ratio: 100 / 68; max-width: 100%;
	background: radial-gradient(ellipse at 50% 34%, #17512f, #0a2013 76%);
	border: 1px solid var(--tv-border, rgba(0,220,180,0.3)); border-radius: 5px; overflow: hidden;
	box-shadow: inset 0 0 40px rgba(0,0,0,0.5);
	transition: box-shadow 0.3s;
}
.gd-field-bg { position: absolute; inset: 0; width: 100%; height: 100%; }
.gd-stands { fill: url(#gdCrowd); }
.gd-grass { fill: rgba(72,150,92,0.22); }
.gd-mow { fill: rgba(120,190,120,0.07); }
.gd-fence { stroke: rgba(255,255,255,0.5); stroke-width: 0.9; stroke-dasharray: 1.6 1.4; }
.gd-track { stroke: rgba(184,124,72,0.55); stroke-width: 3; }
.gd-foul { stroke: rgba(255,255,255,0.34); stroke-width: 0.5; }
.gd-dirtpath { stroke: rgba(158,100,60,0.72); stroke-width: 3.4; stroke-linejoin: round; }
.gd-dirtcut { fill: rgba(158,100,60,0.72); }
.gd-paths { stroke: rgba(238,210,182,0.45); stroke-width: 0.6; }
.gd-mound { fill: rgba(168,108,64,0.85); }
/* Home-run / walk-off field celebration — glow pulse on the field edge. */
.gd-field.celebrate { box-shadow: inset 0 0 40px rgba(0,0,0,0.5), inset 0 0 30px rgba(255,210,62,0.28), 0 0 22px rgba(255,210,62,0.4); animation: gd-celebrate 0.6s ease-out 3; }
.gd-field.walkoff.celebrate { box-shadow: inset 0 0 40px rgba(0,0,0,0.5), inset 0 0 34px rgba(255,120,62,0.34), 0 0 26px rgba(255,120,62,0.5); }
@keyframes gd-celebrate { 0%,100% { filter: brightness(1); } 50% { filter: brightness(1.25); } }

/* ── Field FX layer (trails, dust, celebration bursts) ───────────────────────── */
.gd-fx { position: absolute; inset: 0; pointer-events: none; z-index: 4; }
.gd-trail, .gd-dust, .gd-burst { position: absolute; pointer-events: none; }
.gd-trail { width: 6px; height: 6px; margin: -3px 0 0 -3px; border-radius: 50%; background: rgba(255,255,255,0.85); box-shadow: 0 0 5px rgba(255,255,255,0.7); animation: gd-trail-fade 0.52s ease-out forwards; }
.gd-trail.hot { background: #ffd23e; box-shadow: 0 0 6px #ffd23e; }
@keyframes gd-trail-fade { from { opacity: 0.9; transform: scale(1); } to { opacity: 0; transform: scale(0.3); } }
.gd-dust { width: 11px; height: 11px; margin: -5.5px 0 0 -5.5px; border-radius: 50%; background: radial-gradient(circle, rgba(200,170,130,0.55), transparent 70%); animation: gd-dust-puff 0.62s ease-out forwards; }
@keyframes gd-dust-puff { from { opacity: 0.8; transform: scale(0.3); } to { opacity: 0; transform: scale(2.4); } }
.gd-burst { width: 14px; height: 14px; margin: -7px 0 0 -7px; border-radius: 50%; border: 2px solid var(--tv-header-color, #37d67a); animation: gd-burst-ring 0.9s ease-out forwards; }
.gd-burst.hot { border-color: #ffd23e; }
@keyframes gd-burst-ring { from { opacity: 0.9; transform: scale(0.2); } to { opacity: 0; transform: scale(3.4); } }
.gd-runner.sliding { animation: gd-slide 0.42s ease-out; }
.gd-batter.sliding { animation: gd-slide 0.42s ease-out; }
@keyframes gd-slide { 0% { transform: scale(1.5, 0.7); } 60% { transform: scale(0.85, 1.15); } 100% { transform: scale(1); } }

/* Instrument chrome over the field (grid + scanlines + corner reticles). */
.gd-crt-grid {
	position: absolute; inset: 0; pointer-events: none; z-index: 1;
	background:
		repeating-linear-gradient(0deg, transparent 0 12.5%, rgba(255,255,255,0.035) 12.5% calc(12.5% + 1px)),
		repeating-linear-gradient(90deg, transparent 0 12.5%, rgba(255,255,255,0.035) 12.5% calc(12.5% + 1px));
}
.gd-crt-scan { position: absolute; inset: 0; pointer-events: none; z-index: 7; background: repeating-linear-gradient(0deg, transparent 0 2px, rgba(0,0,0,0.16) 2px 3px); }
.gd-reticle { position: absolute; inset: 5px; pointer-events: none; z-index: 8; }
.gd-reticle i { position: absolute; width: 12px; height: 12px; border: 1.5px solid var(--tv-header-color, #37d67a); opacity: 0.6; }
.gd-reticle i:nth-child(1) { top: 0; left: 0; border-right: 0; border-bottom: 0; }
.gd-reticle i:nth-child(2) { top: 0; right: 0; border-left: 0; border-bottom: 0; }
.gd-reticle i:nth-child(3) { bottom: 0; left: 0; border-right: 0; border-top: 0; }
.gd-reticle i:nth-child(4) { bottom: 0; right: 0; border-left: 0; border-top: 0; }

/* Field tokens — absolutely positioned by left/top %, centred via negative margins
   so `transform` stays free for the hop (gd-lift) animation. */
.gd-fielder, .gd-ball, .gd-runner, .gd-batter, .gd-fbase { position: absolute; }
.gd-fielder {
	width: 16px; height: 16px; margin: -8px 0 0 -8px; border-radius: 50%;
	background: rgba(12,28,22,0.94); border: 1.5px solid var(--tv-header-color, #37d67a);
	color: var(--tv-text); font-size: 7px; font-weight: bold; letter-spacing: 0.2px;
	display: flex; align-items: center; justify-content: center;
	box-shadow: 0 0 5px rgba(0,220,180,0.25); transition: left .5s ease, top .5s ease; z-index: 3;
}
.gd-batter {
	width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px; border-radius: 2px; background: #fff;
	box-shadow: 0 0 5px rgba(255,255,255,0.7); transition: left .5s ease, top .5s ease; z-index: 5;
}
.gd-batter.outed { opacity: 0.25; background: #ff5a5a; box-shadow: none; }
.gd-runner {
	width: 10px; height: 10px; margin: -5px 0 0 -5px; border-radius: 50%; background: #ffd23e;
	box-shadow: 0 0 6px rgba(255,210,62,0.75); transition: left .6s ease, top .6s ease; z-index: 5;
}
.gd-runner.outed { background: #ff5a5a; opacity: 0.3; box-shadow: none; }
.gd-ball {
	width: 8px; height: 8px; margin: -4px 0 0 -4px; border-radius: 50%;
	background: radial-gradient(circle at 35% 35%, #fff, #d6d6d6);
	box-shadow: 0 0 6px rgba(255,255,255,0.9); transition: left .34s linear, top .34s linear; z-index: 6;
}
.gd-fbase {
	width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px; transform: rotate(45deg);
	background: rgba(255,255,255,0.16); border: 1px solid rgba(255,255,255,0.45);
	transition: background .2s, box-shadow .2s; z-index: 2;
}
.gd-fbase.home { border-radius: 1px; }
.gd-fbase.lit { background: var(--tv-header-color, #37d67a); border-color: var(--tv-header-color, #37d67a); box-shadow: 0 0 8px var(--tv-header-color, #37d67a); }
.gd-fbase.cross { animation: gd-base-cross 0.5s ease-out; }
@keyframes gd-base-cross { 0%,100% { box-shadow: none; } 40% { background: #ffd23e; box-shadow: 0 0 10px #ffd23e; } }
@keyframes gd-lift { 0% { transform: translateY(0) scale(1); } 50% { transform: translateY(-14px) scale(1.3); } 100% { transform: translateY(0) scale(1); } }

/* ── Instrument rail ─────────────────────────────────────────────────────────── */
.gd-rail { flex: 0 0 176px; display: flex; flex-direction: column; gap: 6px; min-width: 0; overflow-y: auto; }
.gd-card {
	flex: 0 0 auto; padding: 6px 8px 7px; border-radius: 3px;
	background: rgba(0, 0, 0, 0.42); border: 1px solid var(--tv-border, rgba(0,220,180,0.2));
	border-left: 3px solid var(--tv-header-color, #37d67a);
}
.gd-microlabel { display: block; font-size: 8px; font-weight: bold; letter-spacing: 2px; text-transform: uppercase; color: rgba(220,240,235,0.5); margin-bottom: 3px; }
.gd-countcard { display: flex; align-items: baseline; gap: 8px; }
.gd-countcard .gd-microlabel { margin: 0; }
.gd-count-big { font-size: 22px; font-weight: bold; font-variant-numeric: tabular-nums; color: #fff; letter-spacing: 2px; }
.gd-outlabel { margin-left: auto; font-size: 9px; letter-spacing: 1px; text-transform: uppercase; color: rgba(220,240,235,0.55); }
.gd-plotcard { display: flex; flex-direction: column; align-items: center; }
.gd-plotcard .gd-microlabel { align-self: flex-start; }
.gd-plot-inset { width: 100%; max-width: 106px; }
.gd-zone { width: 100%; aspect-ratio: 1; overflow: visible; }
.gd-zone-box { fill: rgba(255,255,255,0.03); stroke: rgba(255,255,255,0.35); stroke-width: 1; }
.gd-zone-grid { stroke: rgba(255,255,255,0.12); stroke-width: 0.5; }
.gd-pitch circle { stroke: rgba(0,0,0,0.6); stroke-width: 1; }
.gd-pitch text { fill: #fff; font-size: 8px; font-weight: bold; text-anchor: middle; font-family: var(--font-mono); }
.gd-pitch { opacity: 1; }
.gd-pitch.new { animation: gd-pitch-drop 0.34s ease-out; transform-box: fill-box; transform-origin: center; }
@keyframes gd-pitch-drop {
	from { opacity: 0; transform: translateY(-14px) scale(1.7); }
	60%  { transform: translateY(0) scale(0.9); }
	to   { opacity: 1; transform: translateY(0) scale(1); }
}
.gd-pitch.ball circle { fill: #3b82f6; }
.gd-pitch.called circle { fill: #f97316; }
.gd-pitch.swinging circle { fill: #ef4444; }
.gd-pitch.foul circle { fill: #eab308; }
.gd-pitch.inplay circle { fill: #22c55e; }

.gd-matchcard { display: flex; flex-direction: column; gap: 4px; }
.gd-mrow { display: flex; align-items: center; gap: 6px; font-size: 11px; }
.gd-mlabel {
	flex: 0 0 auto; width: 24px; text-align: center; font-size: 8px; font-weight: bold;
	letter-spacing: 0.5px; padding: 2px 0; border-radius: 2px;
	background: rgba(255,255,255,0.08); color: rgba(220,240,235,0.6);
}
.gd-mlabel.bat { background: var(--tv-header-color, #37d67a); color: #041008; }
.gd-mname { font-weight: bold; color: var(--tv-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.gd-play {
	flex: 1 1 auto; display: flex; flex-direction: column; justify-content: center; padding: 7px 9px; border-radius: 3px;
	background: rgba(0, 0, 0, 0.42); border: 1px solid var(--tv-border, rgba(0,220,180,0.2));
	border-left: 3px solid var(--tv-header-color, #37d67a); animation: gd-play-in 0.3s ease-out;
}
.gd-play.scored { border-left-color: #ffd23e; box-shadow: inset 0 0 16px rgba(255,210,62,0.14); }
.gd-play.pending { border-left-color: rgba(255,255,255,0.22); animation: none; }
.gd-play.pending .gd-play-desc { color: rgba(220,240,235,0.4); font-size: 13px; letter-spacing: 3px; }
@keyframes gd-play-in { from { opacity: 0; transform: translateX(10px); } to { opacity: 1; transform: translateX(0); } }
.gd-play-desc { font-size: 16px; font-weight: bold; color: #fff; letter-spacing: 0.3px; line-height: 1.15; }
.gd-play.scored .gd-play-desc { color: #ffd23e; }
.gd-play-bat { font-size: 10px; color: rgba(220,240,235,0.6); margin-top: 2px; }

/* ── Announcer lower-third ───────────────────────────────────────────────────── */
.gd-caption {
	flex: 0 0 auto; display: flex; align-items: center; gap: 9px; min-height: 44px; padding: 5px 10px 5px 6px;
	border-radius: 3px; background: rgba(0,0,0,0.6); border: 1px solid var(--tv-border, rgba(0,220,180,0.25));
}
.gd-cap-tag {
	flex: 0 0 auto; font-size: 9px; font-weight: bold; letter-spacing: 1.5px; text-transform: uppercase;
	color: #041008; background: var(--tv-header-color, #37d67a); padding: 3px 8px; border-radius: 2px;
}
/* Comfortably fits up to two lines; the reserved min-height keeps the lower-third from
   jumping as one- and two-line calls alternate. Clamps with an ellipsis beyond two. */
.gd-cap-text {
	flex: 1 1 auto; font-size: 12px; line-height: 1.35; color: var(--tv-text); overflow: hidden;
	display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2;
}
.gd-cap-text.in { animation: gd-cap-in 0.3s ease-out; }
@keyframes gd-cap-in { from { opacity: 0.25; transform: translateY(3px); } to { opacity: 1; transform: translateY(0); } }

/* ── Gameday-native jumbotron card ───────────────────────────────────────────────
   The sports FX cards, rendered as a compact banner over the field (not a full-screen
   takeover) while the Gameday sub-screen is open. Auto-dismisses via `.in` toggle. */
.gd-jumbo {
	position: absolute; left: 50%; top: 42%; transform: translate(-50%, -50%);
	z-index: 9; pointer-events: none; text-align: center; padding: 10px 26px; border-radius: 4px;
	background: rgba(4, 10, 8, 0.86); border: 1px solid var(--tv-header-color, #37d67a);
	box-shadow: 0 0 22px rgba(0,220,180,0.35), inset 0 0 20px rgba(0,0,0,0.5);
	opacity: 0; transition: opacity 0.35s ease-out;
}
.gd-jumbo.in { opacity: 1; animation: gd-jumbo-in 0.45s cubic-bezier(0.2,1.3,0.4,1); }
.gd-jumbo-title { font-size: 26px; font-weight: bold; letter-spacing: 4px; color: var(--tv-header-color, #37d67a); text-shadow: 0 0 14px currentColor; line-height: 1.1; }
.gd-jumbo-sub { font-size: 10px; letter-spacing: 2.5px; text-transform: uppercase; color: var(--tv-text); margin-top: 4px; }
.gd-jumbo.hot { border-color: #ffd23e; box-shadow: 0 0 26px rgba(255,210,62,0.5), inset 0 0 20px rgba(0,0,0,0.5); }
.gd-jumbo.hot .gd-jumbo-title { color: #ffd23e; }
.gd-jumbo.out { border-color: #ff4a5b; box-shadow: 0 0 22px rgba(255,74,91,0.45), inset 0 0 20px rgba(0,0,0,0.5); }
.gd-jumbo.out .gd-jumbo-title { color: #ff5a6b; }
.gd-jumbo.final .gd-jumbo-title { color: #fff; }
@keyframes gd-jumbo-in { 0% { transform: translate(-50%,-50%) scale(0.55); opacity: 0; } 100% { transform: translate(-50%,-50%) scale(1); opacity: 1; } }

/* ── Idle screen (opened before an at-bat) ───────────────────────────────────── */
.gd-idle { flex: 1 1 auto; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; }
.gd-idle-logo { font-size: 30px; color: var(--tv-header-color, #37d67a); text-shadow: 0 0 14px var(--tv-header-color, #37d67a); animation: gd-idle-pulse 1.8s ease-in-out infinite; }
.gd-idle-title { font-size: 14px; font-weight: bold; letter-spacing: 4px; color: var(--tv-text); }
.gd-idle-sub { font-size: 11px; letter-spacing: 1.5px; color: rgba(220,240,235,0.5); }
@keyframes gd-idle-pulse { 0%,100% { opacity: 0.55; } 50% { opacity: 1; } }

/* ── Line score strip (under the score bug) ────────────────────────────────────
   Inning-by-inning runs + R/H totals. Scrolls horizontally into extras; the team
   column sticks so the abbreviations stay put. */
.gd-linescore { flex: 0 0 auto; }
.gd-ls-scroll { overflow-x: auto; overflow-y: hidden; border: 1px solid var(--tv-border, rgba(0,220,180,0.2)); border-radius: 3px; background: rgba(0,0,0,0.4); }
.gd-ls { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }
.gd-ls td { text-align: center; padding: 1px 6px; font-size: 11px; color: rgba(220,240,235,0.8); border-right: 1px solid rgba(255,255,255,0.05); }
.gd-ls .gd-ls-head td { font-size: 8px; font-weight: bold; letter-spacing: 1px; color: rgba(220,240,235,0.4); }
.gd-ls-team { text-align: left !important; font-weight: bold; letter-spacing: 1px; color: var(--tv-text) !important; min-width: 36px; position: sticky; left: 0; background: rgba(3,8,6,0.96); }
.gd-ls-tot { font-weight: bold; color: #fff !important; background: rgba(255,255,255,0.05); }
.gd-ls td.cur { background: rgba(0,220,180,0.14); color: #fff; }
.gd-ls .gd-ls-head td.cur { color: var(--tv-header-color, #37d67a); }
.gd-ls-away td, .gd-ls-home td { border-top: 1px solid rgba(255,255,255,0.05); }

/* ── Win-probability bar (below the field) ─────────────────────────────────────
   A live estimate (not a trained model) — a two-tone bar that swings each at-bat. */
.gd-winprob { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; height: 20px; padding: 0 4px; font-size: 9px; }
.gd-wp-label { flex: 0 0 auto; font-weight: bold; letter-spacing: 1.5px; color: rgba(220,240,235,0.5); text-transform: uppercase; }
.gd-wp-label i { font-style: normal; opacity: 0.55; margin-left: 3px; text-transform: none; letter-spacing: 0; }
.gd-wp-side { flex: 0 0 auto; font-weight: bold; letter-spacing: 0.5px; color: rgba(220,240,235,0.55); font-variant-numeric: tabular-nums; white-space: nowrap; }
.gd-wp-side.lead { color: #fff; }
.gd-wp-bar { flex: 1 1 auto; display: flex; height: 7px; border-radius: 4px; overflow: hidden; border: 1px solid rgba(255,255,255,0.12); }
.gd-wp-bar i { display: block; height: 100%; transition: width 0.5s ease; }
.gd-wp-bar i.a { background: linear-gradient(90deg, rgba(120,150,205,0.55), rgba(120,150,205,0.9)); }
.gd-wp-bar i.h { background: linear-gradient(90deg, var(--tv-header-color, #37d67a), rgba(0,220,180,0.65)); }

/* ── Standings dock (rail card) ─────────────────────────────────────────────────
   Persistent league table in the instrument rail; the two teams in this game pop.
   The rail scrolls if the cards overrun the field height (small TV panels). */
.gd-standcard { flex: 0 0 auto; }
.gd-stand { max-height: 128px; overflow-y: auto; }
.gd-stand-empty { font-size: 10px; color: rgba(220,240,235,0.4); padding: 3px 2px; }
.gd-stand-row { display: flex; align-items: center; gap: 5px; padding: 1.5px 3px; font-size: 10px; color: rgba(220,240,235,0.7); font-variant-numeric: tabular-nums; }
.gd-stand-row.me { color: #fff; background: rgba(0,220,180,0.13); border-radius: 2px; }
.gd-stand-rank { flex: 0 0 12px; text-align: right; color: var(--tv-header-color, #37d67a); }
.gd-stand-team { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gd-stand-rec { flex: 0 0 auto; font-weight: bold; }
.gd-stand-gb { flex: 0 0 26px; text-align: right; color: rgba(220,240,235,0.45); }
.gd-stand-row.me .gd-stand-gb { color: rgba(255,255,255,0.7); }

/* ── Replay framing: ◉ REPLAY bug + camera-cut wipe ─────────────────────────────
   The bug rides while a play animates (it IS a replay of the just-called at-bat) and
   clears when the ball lands; each new at-bat sweeps in as a fresh cut. */
.gd-replaybug {
	position: absolute; top: 7px; left: 8px; z-index: 8; display: none; align-items: center; gap: 5px;
	padding: 2px 7px 2px 6px; border-radius: 2px; background: rgba(4,10,8,0.72);
	font-size: 8.5px; font-weight: bold; letter-spacing: 2px; color: #ff5a6b; text-transform: uppercase;
}
.gd-field.replaying .gd-replaybug { display: flex; }
.gd-replaybug i { width: 7px; height: 7px; border-radius: 50%; background: #ff3344; box-shadow: 0 0 5px #ff3344; animation: gd-rec-blink 1.1s steps(1) infinite; }
@keyframes gd-rec-blink { 0%,50% { opacity: 1; } 51%,100% { opacity: 0.25; } }
.gd-wipe {
	position: absolute; inset: 0; z-index: 20; pointer-events: none;
	background: linear-gradient(105deg, transparent 30%, var(--tv-header-color, #37d67a) 48% 52%, transparent 70%);
	opacity: 0.45; transform: translateX(-115%);
}
.gd-wipe.go { animation: gd-wipe-sweep 0.66s ease-in-out forwards; }
@keyframes gd-wipe-sweep { from { transform: translateX(-115%); } to { transform: translateX(115%); } }

/* ── Standings bug (transient league table) ────────────────────────────────────
   Flashed up periodically during a sports broadcast, top-RIGHT so it blocks the
   play-by-play text (which fills from the top-left) as little as possible. The
   text layer beneath it wraps around its footprint (see .tv-standings-shape) so
   no line runs under the bug. Auto-dismisses client-side; slides in from the
   right. Themed off the channel's TV vars, same look as the score-bug. */
#tv-standings {
	position: absolute;
	top: 52px;
	right: 10px;
	z-index: 46;
	display: none;
	flex-direction: column;
	min-width: 208px;
	pointer-events: none;
	background: rgba(2, 6, 5, 0.94);
	border: 1px solid var(--tv-border);
	border-left: 3px solid var(--tv-header-color);
	border-radius: 2px;
	overflow: hidden;
	box-shadow: 0 2px 14px rgba(0, 0, 0, 0.6);
	animation: tv-st-in 0.35s ease-out;
}
/* While the standings bug is up, the message column runs as block flow so a
   right-floated shape (sized in JS to the bug's footprint) reserves its corner
   and the play-by-play text wraps around it instead of vanishing underneath. */
#tv-content.standings-on #tv-messages { display: block; }
.tv-standings-shape {
	float: right;
	width: var(--tv-st-shape-w, 0px);
	height: var(--tv-st-shape-h, 0px);
	shape-outside: margin-box;
	pointer-events: none;
}
#tv-standings.on { display: flex; }
.tv-st-title {
	font-size: 10px;
	font-weight: bold;
	letter-spacing: 1px;
	text-transform: uppercase;
	color: #05100c;
	background: var(--tv-header-color);
	padding: 3px 9px;
	white-space: nowrap;
}
.tv-st-row {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 2px 9px;
	font-size: 12px;
	line-height: 1.3;
	color: rgba(220, 240, 235, 0.85);
}
.tv-st-row:nth-child(2) { color: #fff; padding-top: 4px; }        /* leader pops */
.tv-st-row:nth-child(2) .tv-st-rec { color: var(--tv-header-color); }
.tv-st-row:last-child { padding-bottom: 4px; }
.tv-st-rank {
	width: 14px;
	text-align: right;
	color: var(--tv-header-color);
	font-variant-numeric: tabular-nums;
}
.tv-st-team { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tv-st-rec { font-weight: bold; font-variant-numeric: tabular-nums; }
.tv-st-rd {
	width: 30px;
	text-align: right;
	font-size: 10px;
	color: rgba(200, 220, 235, 0.55);
	font-variant-numeric: tabular-nums;
}
@keyframes tv-st-in {
	from { opacity: 0; transform: translateX(10px); }
	to   { opacity: 1; transform: translateX(0); }
}

/* ── Sports graphics (full-screen FX) ──────────────────────────────────────────
   Home-run trajectory call-out, final-score card, extra-innings hype. They take
   over the screen area (below header, above footer) for a few seconds, above the
   score/standings bugs, then auto-dismiss. Pure CSS/SMIL — play on insert. */
.tv-fx-host {
	position: absolute;
	top: 44px; bottom: 72px; left: 0; right: 0;
	z-index: 55;
	pointer-events: none;
	overflow: hidden;
	display: none;
}
.tv-fx-host.on { display: block; }

/* Home run: a ball arcs from the plate into the stands, then the call slams in. */
.tv-fx-hr { position: absolute; inset: 0; }
.fx-homerun::before {
	content: ""; position: absolute; inset: 0;
	background: radial-gradient(circle at 90% 24%, rgba(255, 210, 74, 0.28), transparent 55%);
	opacity: 0; animation: tv-hr-flash 0.7s ease-out 1.15s;
}
.tv-fx-hr-svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.tv-fx-hr-arc {
	stroke: var(--tv-header-color); stroke-width: 3; stroke-linecap: round;
	filter: drop-shadow(0 0 4px var(--tv-header-color));
	stroke-dasharray: 640; stroke-dashoffset: 640;
	animation: tv-hr-draw 1.25s ease-out forwards;
}
.tv-fx-hr-ball { fill: #fff; filter: drop-shadow(0 0 5px #fff); }
.tv-fx-hr-burst {
	fill: #ffd24a; opacity: 0; transform-box: fill-box; transform-origin: center;
	animation: tv-hr-burst 0.6s ease-out 1.2s forwards;
}
.tv-fx-hr-title {
	position: absolute; left: 0; right: 0; top: 34%; text-align: center;
	font-family: var(--font-mono); font-weight: 900;
	font-size: clamp(30px, 9vw, 76px); letter-spacing: 4px; color: #fff;
	text-shadow: 0 0 18px var(--tv-header-color), 0 3px 0 rgba(0, 0, 0, 0.55);
	opacity: 0; animation: tv-hr-title 2.5s cubic-bezier(.2, 1.4, .3, 1) 1.2s forwards;
}
.tv-fx-hr-title.grand { color: #ffe66a; text-shadow: 0 0 22px #ffb300, 0 3px 0 rgba(0, 0, 0, 0.55); }
.tv-fx-hr-sub {
	position: absolute; left: 0; right: 0; top: calc(34% + clamp(38px, 11vw, 92px)); text-align: center;
	font-family: var(--font-mono); font-size: 14px; letter-spacing: 3px; text-transform: uppercase;
	color: var(--tv-header-color); opacity: 0; animation: tv-fx-hold 2.2s ease-out 1.6s forwards;
}
@keyframes tv-hr-draw { to { stroke-dashoffset: 0; } }
@keyframes tv-hr-burst { 0% { opacity: 0.9; transform: scale(0.3); } 100% { opacity: 0; transform: scale(9); } }
@keyframes tv-hr-flash { 0% { opacity: 0.85; } 100% { opacity: 0; } }
@keyframes tv-hr-title {
	0%   { opacity: 0; transform: scale(1.6); }
	12%  { opacity: 1; transform: scale(1); }
	16%  { transform: scale(1) translateX(-4px); }
	20%  { transform: translateX(4px); }
	24%  { transform: translateX(0); }
	85%  { opacity: 1; }
	100% { opacity: 0; transform: scale(1.05); }
}
@keyframes tv-fx-hold { 0% { opacity: 0; } 15% { opacity: 1; } 85% { opacity: 1; } 100% { opacity: 0; } }

/* Final-score card. */
.tv-fx-final {
	position: absolute; inset: 0; display: flex; flex-direction: column;
	align-items: center; justify-content: center; gap: 10px;
	background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.82));
	opacity: 0; animation: tv-fx-hold 4.4s ease-out forwards;
}
.tv-fx-final-label {
	font-family: var(--font-mono); font-size: 13px; letter-spacing: 6px;
	color: var(--tv-header-color); text-transform: uppercase;
}
.tv-fx-final-card {
	display: flex; flex-direction: column; gap: 2px; min-width: min(64%, 320px);
	background: rgba(0, 0, 0, 0.6); border: 1px solid var(--tv-border); border-radius: 3px;
	overflow: hidden; transform: scale(0.9);
	animation: tv-final-pop 0.5s cubic-bezier(.2, 1.4, .3, 1) 1.1s forwards;
}
.tv-fx-final-row {
	display: flex; align-items: center; justify-content: space-between; gap: 20px;
	padding: 8px 18px; font-family: var(--font-mono); font-size: clamp(18px, 4vw, 26px);
	color: rgba(220, 240, 235, 0.75);
}
.tv-fx-final-row .sc { font-weight: 900; font-variant-numeric: tabular-nums; }
.tv-fx-final-row.win { color: #fff; background: linear-gradient(90deg, color-mix(in srgb, var(--tv-header-color) 22%, transparent), transparent); }
.tv-fx-final-row.win .sc { color: var(--tv-header-color); }
.tv-fx-final-tag {
	font-family: var(--font-mono); font-weight: 900; font-size: clamp(16px, 5vw, 30px);
	letter-spacing: 3px; color: var(--tv-header-color); text-shadow: 0 0 14px var(--tv-header-color);
	opacity: 0; animation: tv-fx-hold 3.0s ease-out 1.4s forwards;
}
@keyframes tv-final-pop { to { transform: scale(1); } }

/* Extra innings hype card. */
.tv-fx-extras {
	position: absolute; inset: 0; display: flex; flex-direction: column;
	align-items: center; justify-content: center; gap: 8px;
	opacity: 0; animation: tv-fx-hold 3.1s ease-out forwards;
}
.tv-fx-extras::before {
	content: ""; position: absolute; inset: 0; opacity: 0.5;
	background: repeating-linear-gradient(115deg, transparent 0 22px, color-mix(in srgb, var(--tv-header-color) 9%, transparent) 22px 24px);
	animation: tv-extras-slide 0.8s linear infinite;
}
.tv-fx-extras-big {
	position: relative; display: flex; flex-direction: column; align-items: center; line-height: 0.92;
	font-family: var(--font-mono); font-weight: 900; font-size: clamp(28px, 8vw, 60px); letter-spacing: 5px;
	color: #fff; text-shadow: 0 0 18px var(--tv-header-color);
	animation: tv-extras-pulse 0.7s ease-in-out infinite alternate;
}
.tv-fx-extras-big span { color: var(--tv-header-color); }
.tv-fx-extras-sub {
	position: relative; font-family: var(--font-mono); font-size: 13px; letter-spacing: 6px;
	text-transform: uppercase; color: var(--tv-header-color);
}
@keyframes tv-extras-slide { to { transform: translateX(24px); } }
@keyframes tv-extras-pulse { from { transform: scale(1); } to { transform: scale(1.06); } }

/* Walk-off: a celebratory sunburst spins behind the biggest call in the book. */
.tv-fx-walkoff {
	position: absolute; inset: 0; display: flex; flex-direction: column;
	align-items: center; justify-content: center; gap: 8px; overflow: hidden;
	opacity: 0; animation: tv-fx-hold 4.2s ease-out forwards;
}
.tv-fx-wo-rays {
	position: absolute; left: 50%; top: 44%; width: 200%; aspect-ratio: 1;
	transform: translate(-50%, -50%); opacity: 0.5;
	background: repeating-conic-gradient(from 0deg, color-mix(in srgb, var(--tv-header-color) 16%, transparent) 0deg 6deg, transparent 6deg 14deg);
	animation: tv-wo-spin 9s linear infinite;
}
.tv-fx-wo-title {
	position: relative; font-family: var(--font-mono); font-weight: 900;
	font-size: clamp(30px, 10vw, 80px); letter-spacing: 3px; color: #fff;
	text-shadow: 0 0 22px var(--tv-header-color), 0 3px 0 rgba(0, 0, 0, 0.5);
	animation: tv-wo-slam 0.6s cubic-bezier(.2, 1.5, .3, 1) 1.1s both;
}
.tv-fx-wo-sub { position: relative; font-family: var(--font-mono); font-size: 15px; letter-spacing: 3px; text-transform: uppercase; color: var(--tv-header-color); }
.tv-fx-wo-score { position: relative; font-family: var(--font-mono); font-size: 14px; letter-spacing: 2px; color: rgba(220, 240, 235, 0.8); font-variant-numeric: tabular-nums; }
@keyframes tv-wo-spin { to { transform: translate(-50%, -50%) rotate(360deg); } }
@keyframes tv-wo-slam {
	0%   { opacity: 0; transform: scale(2.2); }
	55%  { opacity: 1; transform: scale(0.94); }
	70%  { transform: scale(1.03) translateX(-3px); }
	82%  { transform: translateX(3px); }
	100% { transform: scale(1) translateX(0); }
}

/* Pre-game matchup card: the two clubs slide in from opposite sides to face off. */
.tv-fx-matchup {
	position: absolute; inset: 0; display: flex; flex-direction: column;
	align-items: center; justify-content: center; gap: 12px;
	background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.8));
	opacity: 0; animation: tv-fx-hold 4.0s ease-out forwards;
}
.tv-fx-mu-label { font-family: var(--font-mono); font-size: 12px; letter-spacing: 6px; text-transform: uppercase; color: var(--tv-header-color); }
.tv-fx-mu-teams { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.tv-fx-mu-team { display: flex; align-items: baseline; gap: 12px; font-family: var(--font-mono); opacity: 0; }
.tv-fx-mu-team.away { transform: translateX(-26px); animation: tv-mu-in 0.5s ease-out 0.1s forwards; }
.tv-fx-mu-team.home { transform: translateX(26px); animation: tv-mu-in 0.5s ease-out 0.28s forwards; }
.tv-fx-mu-team .nm { font-weight: 900; font-size: clamp(20px, 5vw, 34px); color: #fff; text-shadow: 0 0 12px var(--tv-header-color); }
.tv-fx-mu-team .rec { font-size: 14px; color: var(--tv-header-color); font-variant-numeric: tabular-nums; }
.tv-fx-mu-vs { font-family: var(--font-mono); font-size: 13px; letter-spacing: 3px; text-transform: uppercase; color: rgba(220, 240, 235, 0.5); }
@keyframes tv-mu-in { to { opacity: 1; transform: translateX(0); } }

/* Double play: a short, snappy flash — not a full takeover (it happens often). */
.tv-fx-dp {
	position: absolute; inset: 0; display: flex; flex-direction: column;
	align-items: center; justify-content: center; gap: 4px;
	opacity: 0; animation: tv-fx-hold 2.4s ease-out forwards;
}
.tv-fx-dp-title {
	font-family: var(--font-mono); font-weight: 900; font-size: clamp(22px, 6vw, 44px);
	letter-spacing: 3px; color: #fff; text-shadow: 0 0 14px var(--tv-header-color);
	animation: tv-dp-pop 0.45s cubic-bezier(.2, 1.5, .3, 1) 0.55s both;
}
.tv-fx-dp-sub { font-family: var(--font-mono); font-size: 12px; letter-spacing: 4px; text-transform: uppercase; color: var(--tv-header-color); }
@keyframes tv-dp-pop {
	0%   { opacity: 0; transform: scale(1.7) rotate(-4deg); }
	60%  { opacity: 1; transform: scale(0.96) rotate(1deg); }
	100% { transform: scale(1) rotate(0); }
}

/* World Series pre-game card — gold-accented, the two finalists face off. */
.tv-fx-ws {
	position: absolute; inset: 0; display: flex; flex-direction: column;
	align-items: center; justify-content: center; gap: 10px;
	background: radial-gradient(ellipse at center, rgba(20, 14, 0, 0.55), rgba(0, 0, 0, 0.85));
	opacity: 0; animation: tv-fx-hold 4.6s ease-out forwards;
}
.tv-fx-ws-banner {
	font-family: var(--font-mono); font-weight: 900; font-size: clamp(26px, 7vw, 52px);
	letter-spacing: 6px; color: #ffe066; text-shadow: 0 0 22px #ffb300, 0 3px 0 rgba(0, 0, 0, 0.5);
	transform: scale(0.9); animation: tv-final-pop 0.5s cubic-bezier(.2, 1.4, .3, 1) 0.1s forwards;
}
.tv-fx-ws-sub { font-family: var(--font-mono); font-size: 12px; letter-spacing: 5px; text-transform: uppercase; color: #ffd24a; }
.tv-fx-ws .tv-fx-mu-team .nm { text-shadow: 0 0 12px #ffb300; }
.tv-fx-ws .tv-fx-mu-team .rec { color: #ffd24a; }

/* World Series champion — trophy drops in, sunburst behind, gold everywhere. */
.tv-fx-champ {
	position: absolute; inset: 0; display: flex; flex-direction: column;
	align-items: center; justify-content: center; gap: 6px; overflow: hidden;
	background: radial-gradient(ellipse at center, rgba(24, 16, 0, 0.5), rgba(0, 0, 0, 0.85));
	opacity: 0; animation: tv-fx-hold 5.4s ease-out forwards;
}
.tv-fx-champ-rays {
	position: absolute; left: 50%; top: 44%; width: 220%; aspect-ratio: 1;
	transform: translate(-50%, -50%); opacity: 0.45;
	background: repeating-conic-gradient(from 0deg, rgba(255, 200, 60, 0.16) 0deg 6deg, transparent 6deg 15deg);
	animation: tv-wo-spin 12s linear infinite;
}
.tv-fx-champ-trophy { position: relative; font-size: clamp(40px, 11vw, 86px); filter: drop-shadow(0 0 18px #ffb300); animation: tv-champ-trophy 0.7s cubic-bezier(.2, 1.5, .3, 1) 1.1s both; }
.tv-fx-champ-team { position: relative; font-family: var(--font-mono); font-weight: 900; font-size: clamp(24px, 6vw, 48px); color: #fff; text-shadow: 0 0 20px #ffb300; }
.tv-fx-champ-label { position: relative; font-family: var(--font-mono); font-size: 14px; letter-spacing: 5px; text-transform: uppercase; color: #ffe066; }
.tv-fx-champ-score { position: relative; font-family: var(--font-mono); font-size: 13px; letter-spacing: 2px; color: rgba(235, 220, 180, 0.8); font-variant-numeric: tabular-nums; }
@keyframes tv-champ-trophy {
	0%   { opacity: 0; transform: scale(2) rotate(-12deg); }
	60%  { opacity: 1; transform: scale(0.95) rotate(4deg); }
	100% { transform: scale(1) rotate(0); }
}

/* Lower-third nameplate */
.tv-overlay-text-card {
	text-align: center;
	background: rgba(0, 0, 0, 0.88);
	color: var(--tv-text, #e0e0e0);
	font-size: 15px;
	font-weight: 600;
	line-height: 1.6;
	letter-spacing: 0.04em;
	padding: 28px 36px;
	white-space: pre-wrap;
	border: 2px solid var(--tv-header-color, #aaa);
	max-width: 80%;
	animation: tv-lt-slide-in 0.3s ease-out;
}

.tv-overlay-lower-third {
	position: absolute;
	bottom: 30px; /* just above the ticker */
	left: 0;
	right: 0;
	background: rgba(0, 0, 0, 0.88);
	border-top: 3px solid var(--tv-header-color);
	padding: 7px 18px 9px;
	animation: tv-lt-slide-in 0.35s ease-out;
}
.tv-overlay-lt-name {
	font-size: 14px;
	font-weight: bold;
	color: var(--tv-header-color);
	text-transform: uppercase;
	letter-spacing: 2px;
}
.tv-overlay-lt-sub {
	font-size: 11px;
	color: var(--tv-text);
	letter-spacing: 1px;
	margin-top: 3px;
	opacity: 0.75;
}
@keyframes tv-lt-slide-in {
	0% { transform: translateY(110%); opacity: 0; }
	100% { transform: translateY(0); opacity: 1; }
}
/* Full-screen alert flash */
.tv-overlay-alert {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	background: rgba(160, 0, 0, 0.92);
	color: #ffffff;
	text-align: center;
	padding: 20px;
	animation: tv-alert-in 0.25s ease-out;
}
.tv-overlay-alert-title {
	font-size: 20px;
	font-weight: bold;
	letter-spacing: 4px;
	text-transform: uppercase;
	line-height: 1.3;
}
.tv-overlay-alert-sub {
	font-size: 13px;
	letter-spacing: 1px;
	margin-top: 8px;
	opacity: 0.85;
}
@keyframes tv-alert-in {
	0% { opacity: 0; transform: scale(1.04); }
	100% { opacity: 1; transform: scale(1); }
}

/* ── TV Static + Knob ── */
#tv-static {
	display: none;
	position: absolute;
	inset: 0;
	z-index: 20;
	pointer-events: none;
	background-color: var(--tv-bg, #111);
	background-image:
		url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='1'/%3E%3C/svg%3E");
	background-size: 180px 180px;
}
#tv-static.tv-static-on {
	display: block;
	animation: tv-static-flicker 0.12s steps(1) infinite;
}
#tv-static.tv-static-fade {
	display: block;
	animation: tv-static-out 0.4s ease-out forwards;
}
@keyframes tv-static-flicker {
	0%   { background-position: 0 0;        filter: brightness(0.88) contrast(1.3); }
	12%  { background-position: -47px 23px; filter: brightness(0.95) contrast(1.4); }
	25%  { background-position: 31px -18px; filter: brightness(0.91) contrast(1.3); }
	37%  { background-position: -12px 55px; filter: brightness(0.97) contrast(1.35); }
	50%  { background-position: 62px -7px;  filter: brightness(0.89) contrast(1.4); }
	62%  { background-position: -28px 40px; filter: brightness(0.94) contrast(1.3); }
	75%  { background-position: 17px -44px; filter: brightness(0.96) contrast(1.35); }
	87%  { background-position: -53px 12px; filter: brightness(0.90) contrast(1.4); }
	100% { background-position: 8px -31px;  filter: brightness(0.93) contrast(1.3); }
}
@keyframes tv-static-out {
	0%   { opacity: 1; filter: brightness(1) contrast(2); }
	60%  { opacity: 0.6; filter: brightness(1.4) contrast(1.5); }
	100% { opacity: 0; filter: brightness(1) contrast(1); }
}
#tv-content.tv-hidden { visibility: hidden; }

/* Knob */
#tv-knob-wrap {
	flex-shrink: 0;
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 0 14px 0 12px;
	border-left: 1px solid var(--tv-border);
}
.tv-tune-btn {
	flex-shrink: 0;
	width: 26px;
	height: 26px;
	border-radius: 50%;
	border: 1px solid var(--tv-border);
	background: transparent;
	color: var(--tv-header-color);
	font-size: 16px;
	line-height: 1;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: filter 0.2s, background 0.15s, color 0.5s ease, border-color 0.5s ease;
}
.tv-tune-btn:hover { filter: drop-shadow(0 0 6px var(--tv-header-color)); }
.tv-tune-btn:active { background: var(--tv-header-color); color: var(--tv-bg); }
#tv-knob {
	width: 68px;
	height: 68px;
	cursor: pointer;
	display: block;
	transition: filter 0.2s;
}
#tv-knob:hover { filter: drop-shadow(0 0 6px var(--tv-header-color)); }
.tv-knob-ring {
	fill: none;
	stroke: var(--tv-header-color);
	stroke-width: 2;
	opacity: 0.7;
}
.tv-knob-line {
	stroke: var(--tv-header-color);
	stroke-width: 2.5;
	stroke-linecap: round;
}
#tv-knob.tv-knob-spinning {
	animation: tv-knob-spin 1.1s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes tv-knob-spin {
	0%   { transform: rotate(0deg); }
	30%  { transform: rotate(200deg); }
	55%  { transform: rotate(310deg); }
	70%  { transform: rotate(270deg); }
	82%  { transform: rotate(340deg); }
	91%  { transform: rotate(320deg); }
	100% { transform: rotate(360deg); }
}
/* CRT shutoff — vertical collapse to bright line, then line shrinks to nothing */
#tv-window.tv-shutting-off {
	animation: tv-crt-shutoff 0.55s ease-in forwards;
	pointer-events: none;
}
@keyframes tv-crt-shutoff {
	0%   { transform: scaleY(1)     scaleX(1);    filter: brightness(1);   }
	10%  { transform: scaleY(0.012) scaleX(1.04); filter: brightness(4);   }
	28%  { transform: scaleY(0.012) scaleX(0.85); filter: brightness(2.5); }
	100% { transform: scaleY(0.012) scaleX(0);    filter: brightness(0);   }
}
/* CRT power-on — reverse of shutoff: line expands to full screen */
#tv-window.tv-powering-on {
	animation: tv-crt-poweron 0.6s ease-out forwards;
	pointer-events: none;
}
@keyframes tv-crt-poweron {
	0%   { transform: scaleY(0.012) scaleX(0);    filter: brightness(0);   }
	20%  { transform: scaleY(0.012) scaleX(0.85); filter: brightness(2.5); }
	40%  { transform: scaleY(0.012) scaleX(1.04); filter: brightness(4);   }
	60%  { transform: scaleY(0.4)   scaleX(1);    filter: brightness(2);   }
	80%  { transform: scaleY(1.02)  scaleX(1);    filter: brightness(1.3); }
	100% { transform: scaleY(1)     scaleX(1);    filter: brightness(1);   }
}

/* ── TV panel: mobile overrides ─────────────────────────────────────────────── */
@media (max-width: 600px) {
	#tv-window {
		/* Square-ish: fills most of the viewport width, height matches */
		width: min(96vw, 96vh);
		aspect-ratio: 1 / 1;
	}
	#tv-header {
		padding: 5px 8px;
		gap: 6px;
	}
	#tv-station-name { font-size: 10px; letter-spacing: 1px; }
	#tv-channel-num  { font-size: 9px; }
	#tv-program-name { font-size: 9px; display: none; }
	#tv-close-btn    { width: 24px; height: 24px; font-size: 13px; }
	#tv-read-btn     { width: 24px; height: 24px; font-size: 11px; }
	#tv-schedule-btn { width: 24px; height: 24px; font-size: 11px; }
	#tv-live-badge   { font-size: 8px; }
	#tv-content      { padding: 8px 10px; }
	#tv-footer       { height: 48px; }
	#tv-knob         { width: 42px; height: 42px; }
	#tv-knob-wrap    { gap: 5px; padding: 0 8px 0 8px; }
	.tv-tune-btn     { width: 22px; height: 22px; font-size: 13px; }
	#tv-tuner-wrap   { padding: 0 4px; }
	#tv-freq-display { font-size: 9px; min-width: 22px; }
	#tv-overlay-container { padding-bottom: 148px; }
}

/* ── Architect Music Player (AMP) ───────────────────────────────────────────── */

#musicplayer-panel {
	display: none;
	position: fixed;
	right: 24px;
	top: 80px;
	/* Above the Tablet OS chassis (z-index 9200, set in tablet-os.js) so Music can
	   be launched from the tablet and float over it, yet below the confirm/toast
	   layers (9600+). */
	z-index: 9250;
	filter: drop-shadow(0 8px 32px rgba(0,0,0,0.7));
}
#musicplayer-panel.active { display: block; }

.amp-body {
	position: relative;   /* anchor for the cassette glass/deck overlays */
	width: 300px;
	background: linear-gradient(160deg, var(--bg3) 0%, var(--bg2) 60%, var(--bg) 100%);
	border: 1px solid var(--border);
	border-radius: 14px;
	overflow: hidden;
	font-family: 'Courier New', Courier, monospace;
	user-select: none;
}

/* Header */
.amp-header {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: 10px 14px 8px;
	background: var(--bg2);
	border-bottom: 1px solid var(--border);
	cursor: move;
}
.amp-branding { display: flex; flex-direction: column; line-height: 1; }
.amp-brand-main {
	font-size: 13px;
	font-weight: 700;
	letter-spacing: 3px;
	color: var(--accent);
	text-shadow: 0 0 8px var(--accent-dim);
}
.amp-brand-sub {
	font-size: 8px;
	letter-spacing: 2px;
	color: var(--text-dim);
	margin-top: 2px;
}
.amp-header-right { display: flex; align-items: center; gap: 8px; }
.amp-screw {
	width: 7px; height: 7px;
	border-radius: 50%;
	background: radial-gradient(circle at 35% 35%, var(--text-dim), var(--bg3));
	border: 1px solid var(--border);
	display: inline-block;
}
.amp-close-btn {
	background: none;
	border: none;
	color: var(--text-dim);
	font-size: 13px;
	cursor: pointer;
	padding: 2px 4px;
	line-height: 1;
	border-radius: 3px;
	transition: color 0.15s;
}
.amp-close-btn:hover { color: #ff4444; }
.amp-help-btn {
	background: none;
	border: 1px solid var(--border);
	color: var(--text-dim);
	font-size: 10px;
	font-weight: 700;
	width: 16px; height: 16px;
	line-height: 1;
	padding: 0;
	border-radius: 50%;
	cursor: pointer;
	transition: color 0.15s, border-color 0.15s, box-shadow 0.15s;
}
.amp-help-btn:hover {
	color: var(--accent);
	border-color: var(--accent);
	box-shadow: 0 0 8px var(--accent-dim);
}

/* About .MOD — ancient-artifact lore, popped out as a centred modal so the relic
   text reads large. Full-viewport dimmed backdrop; the green CRT card floats in
   the middle. Sits above the AMP panel (9250) but below confirm/toast (9600+). */
.amp-about {
	position: fixed;
	inset: 0;
	z-index: 9550;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 24px;
	background: rgba(0, 6, 0, 0.72);
	backdrop-filter: blur(3px);
	-webkit-backdrop-filter: blur(3px);
	font-family: 'Courier New', Courier, monospace;
}
.amp-about[hidden] { display: none; }
/* The green phosphor card itself */
.amp-about-card {
	position: relative;
	width: min(92vw, 560px);
	max-height: 82vh;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	background: linear-gradient(160deg, #060e06 0%, #030703 100%);
	color: #2aad10;
	border: 1px solid #1f7d0c;
	border-radius: 12px;
	box-shadow: 0 0 0 1px rgba(57,255,20,0.15), 0 18px 60px rgba(0,0,0,0.75),
		0 0 40px rgba(42,173,16,0.25);
	animation: amp-about-pop 0.18s ease-out;
}
@keyframes amp-about-pop {
	from { opacity: 0; transform: scale(0.96); }
	to   { opacity: 1; transform: scale(1); }
}
/* Faint CRT scanlines drifting over the relic text */
.amp-about-scanline {
	position: absolute;
	inset: 0;
	pointer-events: none;
	background: repeating-linear-gradient(0deg,
		rgba(57,255,20,0.05) 0px, rgba(57,255,20,0.05) 1px, transparent 1px, transparent 3px);
	mix-blend-mode: screen;
}
.amp-about-head {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: 16px 20px 12px;
	border-bottom: 1px solid #1a2e1a;
}
.amp-about-title {
	font-size: 16px;
	font-weight: 700;
	letter-spacing: 3px;
	color: #39ff14;
	text-shadow: 0 0 8px rgba(57,255,20,0.5);
}
.amp-about .amp-close-btn { font-size: 18px; }
.amp-about-body {
	padding: 18px 24px 24px;
	overflow-y: auto;
	font-size: 15px;
	line-height: 1.75;
	letter-spacing: 0.3px;
	text-shadow: 0 0 4px rgba(42,173,16,0.35);
}
.amp-about-body p { margin: 0 0 14px; }
.amp-about-relic {
	color: #1f7d0c;
	font-size: 12px;
	letter-spacing: 2px;
	text-align: center;
	text-transform: uppercase;
	margin-bottom: 18px !important;
}
.amp-about-hi {
	color: #39ff14;
	text-shadow: 0 0 6px rgba(57,255,20,0.6);
}
.amp-about-howto {
	margin-top: 6px;
	padding-top: 14px;
	border-top: 1px dashed #1a2e1a;
	color: #39ff14;
}

/* Cassette window */
.amp-cassette-window {
	margin: 12px 14px;
	background: var(--bg);
	border: 1px solid var(--border);
	border-radius: 8px;
	/* Taller window so the tape shell reads as a chunky block reaching up to the
	   glass top edge (reels stay centred, mask holes stay aligned over them). */
	padding: 44px 16px;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0;
	position: relative;
	perspective: 380px;
}
.amp-cassette-body {
	position: absolute;
	inset: 4px;
	border-radius: 6px;
	/* Graphite walkman tape shell — deliberately a different colour from the
	   theme-tinted chassis window so the cassette reads as its own object.
	   Bored spool-hole rims: light crescents on the upper lip of each hole, dark
	   crescents on the lower lip. They're offset up/down from each hole centre so
	   the mask (below) clips their inner half, leaving directional rims that — with
	   the Z-lift in the drop animation — read as real 3D bores as the tape tilts in. */
	background:
		radial-gradient(circle 32px at 40px calc(50% - 6px), rgba(210, 228, 255, 0.28) 55%, rgba(210, 228, 255, 0) 82%),
		radial-gradient(circle 32px at 40px calc(50% + 7px), rgba(0, 0, 0, 0.72) 52%, rgba(0, 0, 0, 0) 84%),
		radial-gradient(circle 32px at calc(100% - 40px) calc(50% - 6px), rgba(210, 228, 255, 0.28) 55%, rgba(210, 228, 255, 0) 82%),
		radial-gradient(circle 32px at calc(100% - 40px) calc(50% + 7px), rgba(0, 0, 0, 0.72) 52%, rgba(0, 0, 0, 0) 84%),
		radial-gradient(ellipse 34px 22px at 50% calc(50% - 5px), rgba(210, 228, 255, 0.22) 55%, rgba(210, 228, 255, 0) 82%),
		radial-gradient(ellipse 34px 22px at 50% calc(50% + 6px), rgba(0, 0, 0, 0.68) 52%, rgba(0, 0, 0, 0) 84%),
		linear-gradient(180deg, #2b2f3a 0%, #14161d 100%);
	border: 1px solid #3d4354;
	display: flex;
	align-items: center;
	justify-content: center;
	/* Above the deck reels (z2) so the shell reads as the front face of the tape;
	   two spool-window holes are punched through it (below) so the fixed reel
	   plugs show through, and slide into alignment as the tape drops in. */
	z-index: 3;
	/* Chunky, thick-shell read: a top bevel highlight + a deep bottom inner shade
	   give the graphite shell real depth so it reads as a solid 3D block, not a
	   flat panel. (Outer drop-shadow is omitted here — the mask below would clip it.) */
	box-shadow:
		inset 0 2px 0 rgba(255,255,255,0.10),
		inset 0 -3px 5px rgba(0,0,0,0.55),
		inset 0 0 12px rgba(0,0,0,0.6);
	/* Reel-hub holes: transparent circles centred 40px in from each shell edge —
	   exactly over the two deck reels — so the spinning plugs show through. The
	   holes ride with the shell, so they line up onto the plugs during the drop.
	   A third, elliptical hole is punched in the centre over the capstan/tape-
	   guide cluster so that section reads as an actual window in the shell too,
	   instead of the shell sitting flush over it. */
	-webkit-mask:
		radial-gradient(circle 26px at 40px 50%, transparent 0 96%, #000 100%),
		radial-gradient(circle 26px at calc(100% - 40px) 50%, transparent 0 96%, #000 100%),
		radial-gradient(ellipse 30px 20px at 50% 50%, transparent 0 90%, #000 100%);
	-webkit-mask-composite: source-in, source-in;
	mask:
		radial-gradient(circle 26px at 40px 50%, transparent 0 96%, #000 100%),
		radial-gradient(circle 26px at calc(100% - 40px) 50%, transparent 0 96%, #000 100%),
		radial-gradient(ellipse 30px 20px at 50% 50%, transparent 0 90%, #000 100%);
	mask-composite: intersect, intersect;
	opacity: 0;
	pointer-events: none;
	transition: opacity 0.22s ease-out;
}
.amp-cassette-body.loaded {
	opacity: 1;
	pointer-events: auto;
}
/* Cassette drop-in / pop-out choreography (see .inserting / .ejecting below) */
/* The tape tips forward (top toward viewer) as it comes down, then flattens as it
   slides home behind the glass — hinging on its bottom edge for the 3D lean. */
@keyframes amp-cassette-drop {
	0%   { opacity: 0; transform: translateY(-150%) translateZ(34px) rotateX(-34deg); }
	12%  { opacity: 1; }
	58%  { transform: translateY(-6%) translateZ(30px) rotateX(-30deg); }
	76%  { transform: translateY(7%) translateZ(17px) rotateX(-7deg); }
	90%  { transform: translateY(-2%) translateZ(6px) rotateX(0deg); }
	100% { opacity: 1; transform: translateY(0) translateZ(0) rotateX(0deg); }
}
@keyframes amp-cassette-rise {
	0%   { opacity: 1; transform: translateY(0) translateZ(0) rotateX(0deg); }
	25%  { transform: translateY(6%) translateZ(22px) rotateX(-10deg); }
	100% { opacity: 0; transform: translateY(-150%) translateZ(34px) rotateX(-34deg); }
}
.amp-cassette-window.inserting .amp-cassette-body {
	opacity: 1;
	transform-origin: center bottom;
	animation: amp-cassette-drop 1.05s cubic-bezier(.34,1.3,.5,1) both;
}
.amp-cassette-window.ejecting .amp-cassette-body {
	opacity: 1;
	transform-origin: center bottom;
	animation: amp-cassette-rise 0.64s cubic-bezier(.5,-0.2,.7,0.5) both;
}
.amp-cassette-window.inserting .amp-empty-hint,
.amp-cassette-window.ejecting .amp-empty-hint { opacity: 0; }
/* Glass cover lid — hinge at bottom, top tilts toward viewer when closed.
   deck-opening swings it fully open so reels stay visible throughout. */
.amp-glass-cover {
	position: absolute;
	inset: 0;
	border-radius: 8px;
	/* Semi-transparent smoked glass: a faint tint plus a diagonal sheen streak
	   so the pane reads as real glass, yet the reels stay visible through it. */
	background:
		linear-gradient(115deg,
			transparent 0%, transparent 38%,
			rgba(200,245,255,0.14) 47%, rgba(200,245,255,0.05) 52%,
			transparent 60%, transparent 100%),
		linear-gradient(160deg,
			rgba(180,240,255,0.08) 0%,
			rgba(80,180,255,0.03) 45%,
			rgba(0,180,255,0.06) 100%
		);
	border-top: 3px solid rgba(160,230,255,0.55);
	border-left: 1px solid rgba(0,240,255,0.10);
	border-right: 1px solid rgba(0,240,255,0.10);
	border-bottom: 1px solid rgba(0,0,0,0.25);
	box-shadow:
		inset 0 1px 0 rgba(255,255,255,0.14),
		inset 0 -1px 0 rgba(0,0,0,0.2);
	transform-origin: bottom center;
	/* At rest the pane sits flush against the shell (no tilt); it only swings out
	   to show its 3D angle while the deck is opening/inserting/ejecting. */
	transform: rotateX(0deg);
	transition: transform 0.7s ease-in-out, border-top-width 0.7s ease-in-out;
	z-index: 5;
	pointer-events: none;
}
.amp-cassette-window.deck-opening .amp-glass-cover,
.amp-cassette-window.inserting .amp-glass-cover,
.amp-cassette-window.ejecting  .amp-glass-cover {
	transform: rotateX(-78deg);
	border-top-width: 9px;
}
/* Empty slot indicator */
.amp-empty-hint {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 8px;
	letter-spacing: 2.5px;
	color: var(--text-dim);
	opacity: 0.3;
	z-index: 1;
	pointer-events: none;
	transition: opacity 0.2s;
}
.amp-cassette-body.loaded ~ .amp-empty-hint { opacity: 0; }
/* Dim the deck hardware when no tape loaded */
.amp-reel { opacity: 0.3; }
.amp-tape-guide,
.amp-tape-head { opacity: 0.3; }
.amp-cassette-body.loaded ~ .amp-reel { opacity: 1; }
.amp-cassette-body.loaded ~ .amp-tape-center .amp-tape-guide,
.amp-cassette-body.loaded ~ .amp-tape-center .amp-tape-head { opacity: 1; }
/* Locked in: the capstan/guides rise up out of their recessed sockets — from the
   sunken background state to a proud, metallic, top-lit pin — as the tape seats;
   eject reverses it. */
.amp-cassette-body.loaded ~ .amp-tape-center .amp-tape-guide,
.amp-cassette-body.loaded ~ .amp-tape-center .amp-tape-head {
	transform: translateY(-1px) scaleY(1);
	filter: brightness(1);
	/* Popped proud: a real cast shadow BELOW the pin (it stands off the shell) +
	   a crisp top-lit highlight lip, so it reads as extended toward you rather
	   than sunk. A faint accent glow says the transport is energised. */
	box-shadow:
		0 3px 5px rgba(0,0,0,0.75),
		0 1px 1px rgba(0,0,0,0.5),
		inset 0 1.5px 0 rgba(255,255,255,0.55),
		inset 0 -2px 3px rgba(0,0,0,0.30),
		0 0 5px var(--accent-dim);
}
/* On eject the capstans sink back into their sockets FIRST (fast), so they're
   already recessed as the tape lifts away. Same specificity as the .loaded rule
   above but placed after it, so it wins at the very start of the eject. */
.amp-cassette-window.ejecting .amp-tape-center .amp-tape-guide,
.amp-cassette-window.ejecting .amp-tape-center .amp-tape-head {
	transform: translateY(3px) scaleY(0.4);
	filter: brightness(0.55);
	box-shadow: inset 0 -2px 3px rgba(0, 0, 0, 0.85);
	transition: transform 0.16s ease-in, filter 0.16s ease-in, box-shadow 0.16s ease-in;
}
.amp-cassette-label-strip {
	/* Stuck-on paper label, off-white with dark hand-labelled text — its own
	   material against the graphite shell. Pinned to the lower face of the shell
	   (clear of the centred reels/head, and rides with the drop-in animation). */
	position: absolute;
	bottom: 5px;
	left: 50%;
	transform: translateX(-50%);
	z-index: 4;
	background: linear-gradient(135deg, #efe7d0 0%, #d8ceb2 100%);
	border: 1px solid #b8ad8c;
	border-radius: 2px;
	padding: 3px 10px;
	font-size: 7px;
	color: #2a2318;
	letter-spacing: 1.5px;
	text-transform: uppercase;
	max-width: 55%;
	text-align: center;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	box-shadow: 0 1px 3px rgba(0,0,0,0.5), inset 0 0 0 1px rgba(255,255,255,0.2);
}
.amp-reel {
	width: 56px;
	height: 56px;
	border-radius: 50%;
	border: 2px solid var(--border);
	background:
		radial-gradient(circle at center, var(--bg2) 20%, transparent 20%),
		conic-gradient(
			var(--bg3) 0deg 30deg, transparent 30deg 90deg,
			var(--bg3) 90deg 120deg, transparent 120deg 180deg,
			var(--bg3) 180deg 210deg, transparent 210deg 270deg,
			var(--bg3) 270deg 300deg, transparent 300deg 360deg
		),
		var(--bg);
	position: relative;
	/* Behind the cassette shell (z3) so the plugs show through the shell's spool-
	   window holes; the tape's holes slide into alignment over them on insert, and
	   they keep spinning even when fully loaded. */
	z-index: 2;
	flex-shrink: 0;
	transition: border-color 0.3s, opacity 0.3s;
}
/* Spin-up: the reels break inertia and accelerate from rest over ~0.6s (matching
   the capstan whirr SFX), then hand off to the steady loop with no jump. The
   cubic-bezier starts at zero slope (rest) and ends at slope 2 — exactly the
   cruise angular velocity — and the loop's 60° start = the spin-up's 60° end
   (and 420° ≡ 60°), so both position and speed stay continuous across the
   handoff and on every loop wrap. */
.amp-reel.spinning {
	animation:
		amp-reel-spinup 0.6s cubic-bezier(0.25, 0, 0.5, 0) both,
		amp-reel-spin 1.8s linear 0.6s infinite;
	border-color: var(--accent);
	box-shadow: 0 0 6px var(--accent-dim);
}
/* Right reel counter-rotates: same ramp + cruise, negated angles. */
.amp-reel-r.spinning {
	animation:
		amp-reel-spinup-r 0.6s cubic-bezier(0.25, 0, 0.5, 0) both,
		amp-reel-spin-r 1.8s linear 0.6s infinite;
}
@keyframes amp-reel-spinup   { from { transform: rotate(0);      } to { transform: rotate(60deg);   } }
@keyframes amp-reel-spin     { from { transform: rotate(60deg);  } to { transform: rotate(420deg);  } }
@keyframes amp-reel-spinup-r { from { transform: rotate(0);      } to { transform: rotate(-60deg);  } }
@keyframes amp-reel-spin-r   { from { transform: rotate(-60deg); } to { transform: rotate(-420deg); } }
.amp-reel-hub {
	position: absolute;
	inset: 18px;
	border-radius: 50%;
	background: radial-gradient(circle at 40% 40%, var(--bg3), var(--bg2));
	border: 1px solid var(--border);
}
.amp-tape-center {
	flex: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	padding: 0 4px;
	position: relative;
	/* Behind the tape shell (z3), alongside the reels (z2), so the capstan/head
	   cluster shows through the shell's punched centre hole only — the cassette
	   slides in over it, not behind it. */
	z-index: 2;
}
.amp-tape-guide {
	width: 4px;
	height: 28px;
	/* Accent-coloured capstan at ALL times (never the chassis colour) —
	   top-lit so it reads as a real machined pin. Recessed vs proud is conveyed by
	   shading + scale below, not by swapping the fill. */
	background: linear-gradient(to bottom, var(--accent) 0%, var(--accent) 42%, var(--accent-dim) 100%);
	border-radius: 2px;
	transition: opacity 0.3s;
}
.amp-tape-head {
	width: 10px;
	height: 20px;
	background: linear-gradient(to bottom, var(--accent) 0%, var(--accent) 42%, var(--accent-dim) 100%);
	border-radius: 2px;
	border: 1px solid var(--accent-dim);
	transition: opacity 0.3s;
}
/* Capstan/head sockets: a dark recessed cutout punched through the graphite
   shell behind each centre element, rimmed to match the shell border, so the
   guides/head read as slotting into holes in the tape. Self-aligning per
   element (see the matching mediadeck sockets). */
.amp-tape-guide,
.amp-tape-head {
	position: relative;
	/* Recessed by default: the grey pin sits sunk flush down in its socket —
	   short (grown from the base), dimmed into shadow, with a dark inner-bottom
	   shade and NO cast shadow (nothing standing proud). It grows UPWARD out of
	   the socket when a tape seats (origin bottom), so the .loaded rule reads as
	   the pin extending toward you, not merely brightening in place. */
	transform-origin: bottom center;
	transform: translateY(3px) scaleY(0.4);
	filter: brightness(0.55);
	box-shadow: inset 0 -2px 3px rgba(0, 0, 0, 0.85);
	transition: opacity 0.3s, transform 0.35s ease, box-shadow 0.35s ease, filter 0.35s ease;
}
.amp-tape-guide::before,
.amp-tape-head::before {
	content: '';
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	border-radius: 3px;
	background: var(--bg);
	box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.85), 0 0 0 1px #3d4354;
	z-index: -1;
}
.amp-tape-guide::before { width: 9px; height: 32px; }
.amp-tape-head::before  { width: 16px; height: 24px; }

/* LCD display */
.amp-lcd {
	margin: 0 14px 12px;
	background: #060e06;
	border: 1px solid #1a2e1a;
	border-radius: 6px;
	padding: 8px 12px;
	box-shadow: inset 0 1px 4px rgba(0,0,0,0.6);
}
.amp-lcd-track {
	font-size: 11px;
	letter-spacing: 1px;
	color: #39ff14;
	text-shadow: 0 0 6px rgba(57,255,20,0.5);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	margin-bottom: 4px;
}
.amp-lcd-status {
	font-size: 9px;
	letter-spacing: 1px;
	color: #2aad10;
	text-shadow: 0 0 4px rgba(42,173,16,0.4);
}

/* Transport controls */
.amp-controls {
	display: flex;
	justify-content: center;
	gap: 8px;
	padding: 4px 14px 10px;
}
.amp-btn {
	width: 44px;
	height: 36px;
	background: linear-gradient(160deg, var(--bg3), var(--bg2));
	border: 1px solid var(--border);
	border-radius: 6px;
	color: var(--text-dim);
	font-size: 14px;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: all 0.1s;
	box-shadow: 0 2px 4px rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.04);
}
.amp-btn:hover {
	background: var(--bg3);
	color: var(--text);
	border-color: var(--text-dim);
}
.amp-btn:active {
	transform: translateY(1px);
	box-shadow: 0 1px 2px rgba(0,0,0,0.5);
}
.amp-btn-play {
	width: 54px;
	background: var(--accent-dim);
	border-color: var(--accent);
	color: var(--accent);
	text-shadow: 0 0 6px var(--accent-dim);
}
.amp-btn-play:hover {
	filter: brightness(1.2);
	color: var(--accent);
	border-color: var(--accent);
}
/* Active-state glow: Play lights while playing, Stop while stopped. */
.amp-btn.active {
	color: var(--accent);
	border-color: var(--accent);
	background: var(--accent-dim);
	text-shadow: 0 0 6px var(--accent);
	box-shadow: 0 0 8px var(--accent-dim), inset 0 0 6px var(--accent-dim);
}
/* Brief accent pulse for Next / Prev. */
@keyframes amp-btn-blip {
	0%   { color: var(--accent); border-color: var(--accent);
	       box-shadow: 0 0 10px var(--accent); text-shadow: 0 0 8px var(--accent); }
	100% { }
}
.amp-btn.blip {
	animation: amp-btn-blip 0.2s ease-out;
}

/* Footer bar */
.amp-footer {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 0 14px 12px;
}
.amp-footer-label {
	font-size: 9px;
	letter-spacing: 2px;
	color: var(--text-dim);
	opacity: 0.5;
}
.amp-tracks-btn {
	font-family: 'Courier New', Courier, monospace;
	font-size: 9px;
	letter-spacing: 2px;
	background: none;
	border: 1px solid var(--border);
	border-radius: 4px;
	color: var(--text-dim);
	padding: 4px 10px;
	cursor: pointer;
	transition: all 0.15s;
}
.amp-tracks-btn:hover { color: var(--text); border-color: var(--text-dim); }

/* Track list drawer */
.amp-tracklist {
	display: none;
	max-height: 220px;
	overflow-y: auto;
	border-top: 1px solid var(--border);
	background: var(--bg);
}
.amp-tracklist.open { display: block; }
.amp-tracklist::-webkit-scrollbar { width: 4px; }
.amp-tracklist::-webkit-scrollbar-track { background: var(--bg); }
.amp-tracklist::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }

.amp-track-row {
	display: grid;
	grid-template-columns: 24px 1fr auto;
	align-items: center;
	gap: 8px;
	padding: 7px 14px;
	cursor: pointer;
	border-bottom: 1px solid var(--border);
	transition: background 0.1s;
}
.amp-track-row:hover { background: var(--bg2); }
.amp-track-row.current {
	background: var(--bg2);
	border-left: 2px solid var(--accent);
}
.amp-track-num { font-size: 9px; color: var(--text-dim); opacity: 0.6; }
.amp-track-name { font-size: 9px; letter-spacing: 1px; color: var(--text-dim); }
.amp-track-row.current .amp-track-name { color: var(--accent); }
.amp-track-bpm { font-size: 8px; color: var(--text-dim); opacity: 0.6; }
.amp-no-tracks { padding: 16px; font-size: 10px; color: var(--text-dim); text-align: center; letter-spacing: 2px; }

/* ── Emergency Security Protocol ─────────────────────────────────────────── */

#esp-banner {
	width: 100%;
	background: rgba(150, 0, 0, 0.93);
	color: #fff;
	font-size: 10px;
	font-weight: 700;
	letter-spacing: 2px;
	text-align: center;
	padding: 5px 12px;
	flex-shrink: 0;
	animation: esp-banner-pulse 1.8s ease-in-out infinite;
	text-shadow: 0 0 10px rgba(255, 80, 80, 0.9);
	pointer-events: none;
}
#esp-banner.esp-hidden { display: none; }

@keyframes esp-banner-pulse {
	0%, 100% { background: rgba(130, 0, 0, 0.90); box-shadow: 0 2px 12px rgba(180, 0, 0, 0.5); }
	50%       { background: rgba(200, 15, 15, 0.97); box-shadow: 0 2px 24px rgba(255, 30, 30, 0.85); }
}

body.esp-active #minimap-grid,
body.esp-active #minimap-grid-hud,
body.esp-active #minimap-grid-mob {
	animation: esp-minimap-pulse 1.8s ease-in-out infinite;
}

@keyframes esp-minimap-pulse {
	0%, 100% { box-shadow: 0 0 6px rgba(180, 0, 0, 0.45); filter: sepia(0.25) hue-rotate(300deg) saturate(1.4) brightness(0.92); }
	50%       { box-shadow: 0 0 22px rgba(255, 20, 20, 0.90); filter: sepia(0.45) hue-rotate(300deg) saturate(1.8) brightness(1.02); }
}

body.esp-active #output {
	animation: esp-output-pulse 1.8s ease-in-out infinite;
}

@keyframes esp-output-pulse {
	0%, 100% { box-shadow: inset 0 0 35px rgba(140, 0, 0, 0.07); }
	50%       { box-shadow: inset 0 0 70px rgba(200, 10, 10, 0.20); }
}

.esp-warning {
	color: #ff3b3b;
	font-weight: 700;
	letter-spacing: 1px;
	border-left: 3px solid #cc0000;
	padding: 4px 0 4px 10px;
	margin: 2px 0;
	text-shadow: 0 0 6px rgba(255, 50, 50, 0.45);
	animation: esp-text-flash 0.9s ease-in-out 4;
}

@keyframes esp-text-flash {
	0%, 100% { opacity: 1; }
	50%       { opacity: 0.55; }
}

/* ── Poker / GameTable ──────────────────────────────────────────────────── */

/* The felt (460px) plus the header, dealer, and command bar under it need more
   room than the default 65% cap gives — that clips the action buttons off the
   bottom on shorter windows. Grow the pane specifically when a table is showing. */
#area-pane:has(.poker-table-wrap) {
	max-height: 88%;
}

/* --poker-felt is the base felt hue (set by settings.js: classic green, the
   theme --accent, or a custom pick). Lighter/darker shades are mixed from it so
   any single colour yields a full vignetted felt. */
.poker-table-wrap {
	position: relative;
	width: 100%;
	height: 460px;
	background: radial-gradient(ellipse at center,
		color-mix(in srgb, var(--poker-felt, #1a4a1a) 85%, #fff) 0%,
		color-mix(in srgb, var(--poker-felt, #1a4a1a) 75%, #000) 60%,
		color-mix(in srgb, var(--poker-felt, #1a4a1a) 50%, #000) 100%);
	border: 2px solid color-mix(in srgb, var(--poker-felt, #1a4a1a) 78%, #fff);
	border-radius: 8px;
	overflow: hidden;
	font-family: var(--font-mono);
}

/* Felt inner oval */
.poker-felt {
	position: absolute;
	inset: 18px 14px;
	border-radius: 50%;
	border: 3px solid color-mix(in srgb, var(--poker-felt, #1a4a1a) 82%, #fff);
	box-shadow: inset 0 0 30px rgba(0,0,0,0.25);
	pointer-events: none;
}

/* Deck of cards shown in center */
.poker-deck {
	position: absolute;
	top: 50%;
	left: 50%;
	width: 68px;
	height: 100px;
	transform: translate(-50%, -50%);
	z-index: 2;
}
/* Two half-piles, each a couple of stacked cards, sitting exactly on top of
   each other at rest — reads as one deck until .shuffling splits them. */
.poker-deck-pile {
	position: absolute;
	inset: 0;
	transform: rotate(-5deg);
}
.poker-deck-card {
	position: absolute;
	width: 68px;
	height: 100px;
	background: #fff;
	border: 1px solid #ccc;
	border-radius: 8px;
}
.poker-deck-card::before {
	content: '';
	position: absolute;
	inset: 6px;
	border-radius: 4px;
	background-image: repeating-linear-gradient(
		45deg, var(--accent) 0px, var(--accent) 4px,
		transparent 4px, transparent 9px
	);
	opacity: 0.85;
}
.poker-deck-pile .poker-deck-card:nth-child(1) { top: 0; left: 0; }
.poker-deck-pile .poker-deck-card:nth-child(2) { top: -3px; left: -3px; }
.poker-deck-pile .poker-deck-card:nth-child(3) { top: -6px; left: -6px; }

/* Riffle bridge shuffle — loops for the whole "SHUFFLING UP…" countdown (one
   pass per repeated poker-shuffle synth cue, ~1.8s apart), cut the instant the
   hand actually deals. Bows the two half-piles apart into a bridge (25%), holds
   and flutters them there while thumbs would riffle the cards down (55-69%),
   then releases/cuts them back together into one deck (85-100%) — the classic
   two-handed riffle shuffle, rather than the whole deck just jiggling in place. */
@keyframes poker-deck-bridge-left {
	0%   { transform: rotate(-5deg)  translate(0, 0); }
	25%  { transform: rotate(-24deg) translate(-16px, -12px); }
	55%  { transform: rotate(-22deg) translate(-15px, -11px); }
	62%  { transform: rotate(-19deg) translate(-13px, -9px); }
	69%  { transform: rotate(-24deg) translate(-16px, -12px); }
	85%  { transform: rotate(-9deg)  translate(-3px, 1px); }
	100% { transform: rotate(-5deg)  translate(0, 0); }
}
@keyframes poker-deck-bridge-right {
	0%   { transform: rotate(-5deg) translate(0, 0); }
	25%  { transform: rotate(14deg) translate(16px, -12px); }
	55%  { transform: rotate(12deg) translate(15px, -11px); }
	62%  { transform: rotate(16deg) translate(13px, -9px); }
	69%  { transform: rotate(12deg) translate(16px, -12px); }
	85%  { transform: rotate(-1deg) translate(3px, 1px); }
	100% { transform: rotate(-5deg) translate(0, 0); }
}
.poker-deck.shuffling .poker-deck-pile-left {
	animation: poker-deck-bridge-left 1.8s ease-in-out infinite;
}
.poker-deck.shuffling .poker-deck-pile-right {
	animation: poker-deck-bridge-right 1.8s ease-in-out infinite;
}
/* Individual cards cascade as the riffle releases — each lifts and drops on a
   slightly offset beat so they visibly slide over and under one another rather
   than the two halves moving as solid blocks. The transform composes on top of
   the parent pile's bridge motion (card offsets are relative to the pile). */
@keyframes poker-card-riffle {
	0%, 50%   { transform: translate(0, 0); }
	60%       { transform: translate(2px, -8px); }
	72%       { transform: translate(0, -2px); }
	80%, 100% { transform: translate(0, 0); }
}
.poker-deck.shuffling .poker-deck-card {
	animation: poker-card-riffle 1.8s ease-in-out infinite;
}
.poker-deck.shuffling .poker-deck-pile-left  .poker-deck-card:nth-child(1) { animation-delay: 0s; }
.poker-deck.shuffling .poker-deck-pile-right .poker-deck-card:nth-child(1) { animation-delay: 0.05s; }
.poker-deck.shuffling .poker-deck-pile-left  .poker-deck-card:nth-child(2) { animation-delay: 0.10s; }
.poker-deck.shuffling .poker-deck-pile-right .poker-deck-card:nth-child(2) { animation-delay: 0.15s; }
.poker-deck.shuffling .poker-deck-pile-left  .poker-deck-card:nth-child(3) { animation-delay: 0.20s; }
.poker-deck.shuffling .poker-deck-pile-right .poker-deck-card:nth-child(3) { animation-delay: 0.25s; }

/* Seats — absolute positioned around the oval */
.poker-seat {
	position: absolute;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;
	z-index: 3;
}
.seat-north { top: 8px; left: 50%; transform: translateX(-50%); }
.seat-east  { right: 10px; top: 50%; transform: translateY(-50%); }
.seat-south { bottom: 8px; left: 50%; transform: translateX(-50%); }
.seat-west  { left: 10px; top: 50%; transform: translateY(-50%); }
.seat-empty .seat-name { color: var(--text-dim); font-style: italic; }

.seat-name {
	font-size: 11px;
	color: var(--text-bright);
	letter-spacing: 0.5px;
	white-space: nowrap;
}
.seat-name .dealer-btn {
	background: var(--yellow);
	color: #000;
	font-size: 9px;
	font-weight: bold;
	border-radius: 50%;
	width: 14px;
	height: 14px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	margin-left: 4px;
}
.seat-chips {
	font-size: 11px;
	color: var(--green);
	letter-spacing: 0.5px;
}
.seat-chips.empty { color: var(--text-dim); }
/* Compact chip-disc stack beside a seat's name — a quick read of how deep their
   remaining stack is (grows one disc per ~100 ₵, capped). Discs pile upward. */
.chip-stack {
	position: relative;
	width: 22px;
	height: 26px;
	margin-top: 2px;
}
.chip-stack .chip-disc {
	position: absolute;
	left: 0;
	width: 20px;
	height: 6px;
	border-radius: 50%;
	border: 1px solid rgba(0, 0, 0, 0.45);
	box-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
}
.chip-disc-0 { background: radial-gradient(circle at 50% 35%, #ff6b6b, #b02a2a); }
.chip-disc-1 { background: radial-gradient(circle at 50% 35%, #5aa9ff, #1e5aa8); }
.chip-disc-2 { background: radial-gradient(circle at 50% 35%, #56d06a, #1f7a34); }
.seat-active-bet {
	font-size: 10px;
	color: var(--yellow);
}
.seat-status {
	font-size: 10px;
	color: var(--red);
	letter-spacing: 1px;
}
.seat-viewer .seat-name { color: var(--cyan); }

/* Cards */
.seat-cards {
	display: flex;
	gap: 6px;
	justify-content: center;
}

.poker-card-wrap {
	perspective: 400px;
	width: 68px;
	height: 100px;
}
.poker-card-inner {
	position: relative;
	width: 100%;
	height: 100%;
	transform-style: preserve-3d;
}
.poker-card-front,
.poker-card-back {
	position: absolute;
	inset: 0;
	border-radius: 8px;
	background: #fff;
	border: 1px solid #bbb;
	box-shadow: 0 1px 4px rgba(0,0,0,0.4);
	backface-visibility: hidden;
	display: flex;
	align-items: center;
	justify-content: center;
	flex-direction: column;
	font-family: var(--font-mono);
	font-size: 22px;
	font-weight: bold;
	line-height: 1.1;
}
.poker-card-back {
	background: #fff;
	border-color: #ccc;
	transform: rotateY(0deg);
}
.poker-card-back::before {
	content: '';
	position: absolute;
	inset: 6px;
	border-radius: 4px;
	background-image: repeating-linear-gradient(
		45deg, var(--accent) 0px, var(--accent) 4px,
		transparent 4px, transparent 9px
	);
	opacity: 0.85;
}
.poker-card-front {
	transform: rotateY(180deg);
}
/* Static face-up cards (community, your hole cards, showdown) rotate the inner so
   the front shows. The one-shot deal flip ends at this same angle, so it holds. */
.poker-card-wrap.face-up .poker-card-inner {
	transform: rotateY(180deg);
}
.poker-card-front .rank-top { position: absolute; top: 6px; left: 8px; font-size: 18px; }
.poker-card-front .suit-center { font-size: 32px; }
.poker-card-front .rank-bot { position: absolute; bottom: 6px; right: 8px; font-size: 18px; transform: rotate(180deg); }
.suit-red { color: #cc1111; }
.suit-black { color: #111111; }

/* Deal animations — each direction starts at deck (center) and slides to position.
   The viewer (south) deals face-down then flips. Others stay face-down. */
@keyframes deal-to-south {
	from { transform: translateY(-120px); opacity: 0; }
	to   { transform: translateY(0);      opacity: 1; }
}
@keyframes deal-to-north {
	from { transform: translateY(100px);  opacity: 0; }
	to   { transform: translateY(0);      opacity: 1; }
}
@keyframes deal-to-east {
	from { transform: translateX(-200px); opacity: 0; }
	to   { transform: translateX(0);      opacity: 1; }
}
@keyframes deal-to-west {
	from { transform: translateX(200px);  opacity: 0; }
	to   { transform: translateX(0);      opacity: 1; }
}
@keyframes card-flip-reveal {
	0%   { transform: rotateY(0deg); }
	50%  { transform: rotateY(90deg); }
	100% { transform: rotateY(180deg); }
}

.dealing-south .poker-card-inner {
	animation: deal-to-south 0.35s ease-out both;
}
.dealing-south.card-2 .poker-card-inner { animation-delay: 0.25s; }
.dealing-north .poker-card-inner {
	animation: deal-to-north 0.35s ease-out both;
}
.dealing-north.card-2 .poker-card-inner { animation-delay: 0.25s; }
.dealing-east .poker-card-inner {
	animation: deal-to-east 0.35s ease-out both;
}
.dealing-east.card-2 .poker-card-inner { animation-delay: 0.25s; }
.dealing-west .poker-card-inner {
	animation: deal-to-west 0.35s ease-out both;
}
.dealing-west.card-2 .poker-card-inner { animation-delay: 0.25s; }

/* Viewer card reveal: after deal animation, flip to face-up */
.seat-viewer .dealing-south .poker-card-inner {
	animation:
		deal-to-south 0.35s ease-out both,
		card-flip-reveal 0.35s ease-in-out 0.45s both;
}
.seat-viewer .dealing-south.card-2 .poker-card-inner {
	animation:
		deal-to-south 0.35s ease-out 0.25s both,
		card-flip-reveal 0.35s ease-in-out 0.7s both;
}

/* Community board in center */
.poker-board {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;
	z-index: 3;
}
.poker-board-cards {
	display: flex;
	gap: 6px;
}
.poker-board-label {
	font-size: 9px;
	color: var(--text-dim);
	letter-spacing: 1px;
	text-transform: uppercase;
}
.poker-pot {
	font-size: 11px;
	color: var(--yellow);
	letter-spacing: 0.5px;
}
.poker-phase {
	font-size: 9px;
	color: var(--text-dim);
	letter-spacing: 1px;
	text-transform: uppercase;
}
.poker-status {
	font-size: 10px;
	color: var(--yellow);
	letter-spacing: 1.5px;
	text-transform: uppercase;
	text-shadow: 0 1px 2px #000;
	animation: status-pulse 1.6s ease-in-out infinite;
}
@keyframes status-pulse {
	0%, 100% { opacity: 0.55; }
	50%      { opacity: 1; }
}

/* Speech bubble for last action */
.poker-bubble {
	position: absolute;
	background: rgba(0,0,0,0.75);
	border: 1px solid var(--accent-dim);
	color: var(--accent);
	font-size: 10px;
	padding: 2px 7px;
	border-radius: 3px;
	white-space: nowrap;
	z-index: 10;
	pointer-events: none;
	animation: bubble-fade 4s ease-out forwards;
}
/* Position bubbles just outside each seat */
.seat-north .poker-bubble  { top: -20px; left: 50%; transform: translateX(-50%); }
.seat-east .poker-bubble   { right: -4px; top: -18px; }
.seat-south .poker-bubble  { bottom: -20px; left: 50%; transform: translateX(-50%); }
.seat-west .poker-bubble   { left: -4px; top: -18px; }
@keyframes bubble-fade {
	0%, 60% { opacity: 1; }
	100%     { opacity: 0; }
}

/* Current-turn highlight ring */
.seat-active {
	outline: 2px solid var(--yellow);
	border-radius: 4px;
	outline-offset: 3px;
	animation: active-pulse 1.2s ease-in-out infinite;
}
@keyframes active-pulse {
	0%, 100% { outline-color: rgba(245,230,66,0.9); }
	50%      { outline-color: rgba(245,230,66,0.35); }
}

/* Table header strip */
.poker-header {
	position: absolute;
	top: 4px;
	left: 50%;
	transform: translateX(-50%);
	font-size: 10px;
	color: rgba(255,255,255,0.35);
	letter-spacing: 2px;
	text-transform: uppercase;
	z-index: 4;
	pointer-events: none;
}

/* Dealer figure + speech bubble (top-left corner, opens inward) */
.poker-dealer {
	position: absolute;
	top: 6px;
	left: 8px;
	display: flex;
	align-items: flex-start;
	gap: 6px;
	z-index: 8;
	pointer-events: none;
}
.dealer-avatar {
	font-size: 22px;
	line-height: 1;
	color: #e8e8f0;
	filter: drop-shadow(0 1px 2px rgba(0,0,0,0.6));
}
.dealer-speech {
	position: relative;
	max-width: 250px;
	background: #12121c;
	border: 1px solid var(--accent-dim);
	color: var(--accent);
	font-size: 17px;
	line-height: 1.3;
	padding: 7px 14px;
	border-radius: 14px;
	box-shadow: 0 2px 6px rgba(0,0,0,0.5);
	animation: chat-pop 0.18s ease-out;
}
/* tail pointing left toward the dealer avatar */
.dealer-speech::before {
	content: '';
	position: absolute;
	left: -8px;
	top: 13px;
	border-width: 7px 8px 7px 0;
	border-style: solid;
	border-color: transparent #12121c transparent transparent;
}

/* No dealer at the table — dim the avatar and flag it plainly */
.dealer-absent .dealer-avatar { opacity: 0.35; filter: grayscale(1); }
.dealer-absent-tag {
	color: var(--text-dim) !important;
	border-color: var(--border) !important;
	font-weight: bold;
	letter-spacing: 0.5px;
}

/* Player chat speech bubbles (from `say`) — open inward toward table center */
.poker-chat {
	position: absolute;
	width: 200px;
	background: #f4f1e4;
	color: #16160f;
	font-size: 17px;
	line-height: 1.25;
	padding: 6px 12px;
	border-radius: 12px;
	white-space: normal;
	text-align: center;
	z-index: 13;
	pointer-events: none;
	box-shadow: 0 2px 6px rgba(0,0,0,0.55);
	animation: chat-pop 0.18s ease-out;
}
.poker-chat::after {
	content: '';
	position: absolute;
	border: 5px solid transparent;
}
.seat-north .poker-chat { top: calc(100% + 6px); left: 50%; transform: translateX(-50%); }
.seat-north .poker-chat::after { top: -9px; left: 50%; margin-left: -5px; border-bottom-color: #f4f1e4; border-top: 0; }
.seat-south .poker-chat { bottom: calc(100% + 6px); left: 50%; transform: translateX(-50%); }
.seat-south .poker-chat::after { bottom: -9px; left: 50%; margin-left: -5px; border-top-color: #f4f1e4; border-bottom: 0; }
.seat-east .poker-chat { right: calc(100% + 8px); top: 50%; transform: translateY(-50%); }
.seat-east .poker-chat::after { right: -9px; top: 50%; margin-top: -5px; border-left-color: #f4f1e4; border-right: 0; }
.seat-west .poker-chat { left: calc(100% + 8px); top: 50%; transform: translateY(-50%); }
.seat-west .poker-chat::after { left: -9px; top: 50%; margin-top: -5px; border-right-color: #f4f1e4; border-left: 0; }
@keyframes chat-pop {
	from { opacity: 0; }
}

/* ── Money on the felt — bills & coins ─────────────────────────────────────── */
/* Sized against the felt's own scale reference — the poker card (68x100px ≈ a
   real 63.5x88.9mm card, so ~1.07px/mm). A real bill (156x66mm) and a large
   coin (~30mm) come out to roughly these dimensions at that same scale. */
.money-pile {
	position: relative;
	height: 40px;
	width: 0;          /* zero-width anchor; bills fan out from its center */
	margin: 0 auto;
}
.money-pile .bill,
.money-pile .coin {
	position: absolute;
	left: 0;
	top: 0;
}
.bill {
	width: 60px;
	height: 26px;
	margin-left: -30px;
	border-radius: 3px;
	border: 1px solid rgba(0,0,0,0.6);
	font-family: var(--font-mono);
	font-size: 13px;
	font-weight: bold;
	line-height: 26px;
	text-align: center;
	color: rgba(255,255,255,0.95);
	text-shadow: 0 1px 1px rgba(0,0,0,0.7);
	box-shadow: 0 2px 4px rgba(0,0,0,0.5);
}
.bill-100 { background: #2b2b3a; }
.bill-50  { background: #6b3fa0; }
.bill-20  { background: #2e7d46; }
.bill-10  { background: #2a6bb0; }
.bill-5   { background: #b23b3b; }
.coin {
	width: 24px;
	height: 24px;
	margin-left: -12px;
	margin-top: 2px;
	border-radius: 50%;
	background: radial-gradient(circle at 35% 30%, #ffe9a8, #c9962b 70%);
	border: 1px solid #8a6a1e;
	box-shadow: 0 2px 4px rgba(0,0,0,0.5);
}

/* Bet piles — placed on the felt between each seat and the center */
.bet-money {
	position: absolute;
	z-index: 5;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 1px;
	pointer-events: none;
}
.bet-money .bet-amt {
	font-size: 11px;
	color: var(--yellow);
	text-shadow: 0 1px 2px #000;
}
.bet-north { top: 84px;    left: 50%; transform: translateX(-50%); }
.bet-south { bottom: 84px; left: 50%; transform: translateX(-50%); }
.bet-east  { right: 116px; top: 50%;  transform: translateY(-50%); }
.bet-west  { left: 116px;  top: 50%;  transform: translateY(-50%); }

/* Center pot as a spread of bills */
.poker-pot {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;
}
.pot-amt {
	font-size: 13px;
	color: var(--yellow);
	letter-spacing: 0.5px;
}

/* Toss-in: the whole pile drops onto the felt */
@keyframes money-toss {
	0%   { opacity: 0; transform: translateY(-26px) scale(0.8); }
	70%  { opacity: 1; transform: translateY(3px)   scale(1.04); }
	100% { transform: translateY(0) scale(1); }
}
.money-pile.tossing { animation: money-toss 0.4s ease-out; }

/* Sweep: pot bills gather inward from a wider spread */
@keyframes money-sweep {
	0%   { opacity: 0.2; transform: scale(1.6); }
	100% { opacity: 1;   transform: scale(1); }
}
.money-pile.sweeping { animation: money-sweep 0.5s ease-out; }

/* Winning seat glow at showdown */
.seat-winner {
	border-radius: 4px;
	animation: winner-glow 1s ease-in-out infinite alternate;
}
.seat-winner .seat-name { color: var(--yellow); text-shadow: 0 0 6px rgba(245,230,66,0.7); }
@keyframes winner-glow {
	from { box-shadow: 0 0 4px 1px rgba(245,230,66,0.45); }
	to   { box-shadow: 0 0 14px 4px rgba(245,230,66,0.95); }
}

/* Clickable command bar under the poker table — a distinct glass strip in the
   theme's accent colour, separate from the felt above it. Text/fill stay
   translucency-based (not theme text vars) because the pane behind the table
   is forced dark on every theme (see the :has(.poker-table-wrap) override
   above) — theme vars go dark-on-dark on light themes, translucent white doesn't. */
.poker-cmdbar {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	justify-content: center;
	align-items: center;
	padding: 10px 12px;
	margin-top: 6px;
	background: linear-gradient(180deg,
		color-mix(in srgb, var(--accent) 18%, transparent),
		color-mix(in srgb, var(--accent) 6%, transparent));
	border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
	border-radius: 10px;
	backdrop-filter: blur(6px);
	-webkit-backdrop-filter: blur(6px);
	box-shadow: 0 2px 10px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.06);
}
.poker-cmd {
	font-family: var(--font-mono);
	font-size: 11px;
	letter-spacing: 0.5px;
	color: #eef3ee;
	background: linear-gradient(180deg, rgba(255,255,255,0.12), rgba(255,255,255,0.02));
	border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
	border-radius: 6px;
	padding: 5px 12px;
	cursor: pointer;
	backdrop-filter: blur(3px);
	-webkit-backdrop-filter: blur(3px);
	box-shadow: 0 1px 3px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.14);
	transition: background 0.12s, border-color 0.12s, box-shadow 0.12s, transform 0.08s;
}
.poker-cmd:hover {
	background: linear-gradient(180deg,
		color-mix(in srgb, var(--accent) 35%, rgba(255,255,255,0.14)),
		color-mix(in srgb, var(--accent) 12%, rgba(255,255,255,0.02)));
	border-color: var(--accent);
	box-shadow: 0 0 8px color-mix(in srgb, var(--accent) 55%, transparent), inset 0 1px 0 rgba(255,255,255,0.16);
}
.poker-cmd:active { transform: translateY(1px); box-shadow: 0 1px 2px rgba(0,0,0,0.3); }

/* Highlighted buttons (call dealer) — solid accent fill so they stand out */
.poker-cmd.highlight {
	background: linear-gradient(180deg,
		color-mix(in srgb, var(--accent) 75%, transparent),
		color-mix(in srgb, var(--accent) 45%, transparent));
	border-color: var(--accent);
	color: #fff;
	font-weight: bold;
}
.poker-cmd.highlight:hover {
	background: linear-gradient(180deg, var(--accent), color-mix(in srgb, var(--accent) 60%, transparent));
}

/* ─── Poker table — mobile stacked layout ───────────────────────────────────
   Desktop renders an oval felt with four absolutely-positioned seats, which
   overflows a phone. On narrow viewports we reflow the *same* server HTML into
   a vertical stack (flex + `order`): the table title, then opponents as a
   compact top strip, the community board + pot in the middle, the viewer's own
   hand at the bottom, and big-tap action buttons below. Opponents are pared
   down to name + stack + status — their cards, bet piles, and bubbles drop.
   Gated on width only (not compact density): a compact-density desktop with a
   wide pane should keep the oval; a narrow window here already matches. */
@media (max-width: 720px) {
	#area-pane .poker-table-wrap {
		display: flex;
		flex-wrap: wrap;
		align-content: flex-start;
		height: auto;
		padding: 8px;
		gap: 6px 5px;
	}

	/* Drop the oval chrome and the info we don't surface on mobile */
	#area-pane .poker-felt,
	#area-pane .poker-dealer,
	#area-pane .poker-deck,
	#area-pane .money-pile,
	#area-pane .bet-money,
	#area-pane .chip-stack,
	#area-pane .poker-bubble,
	#area-pane .poker-chat { display: none; }

	/* Pull every seat out of absolute positioning into the flow */
	#area-pane .poker-seat {
		position: static;
		transform: none;
		z-index: auto;
		gap: 2px;
	}

	/* Table title — small centered header, first in the stack */
	#area-pane .poker-header {
		position: static;
		transform: none;
		order: 0;
		flex: 1 1 100%;
		text-align: center;
		margin-bottom: 2px;
	}

	/* Opponents (every seat but the viewer's south seat): compact top strip */
	#area-pane .poker-seat:not(.seat-south) {
		order: 1;
		flex: 1 1 0;
		min-width: 0;
		flex-direction: column;
		justify-content: center;
		padding: 4px 3px;
		background: rgba(0, 0, 0, 0.28);
		border: 1px solid rgba(255, 255, 255, 0.08);
		border-radius: 6px;
		overflow: hidden;
	}
	#area-pane .poker-seat:not(.seat-south) .seat-name,
	#area-pane .poker-seat:not(.seat-south) .seat-chips {
		font-size: 10px;
		max-width: 100%;
		overflow: hidden;
		text-overflow: ellipsis;
	}
	/* Opponent hole cards are hidden on mobile — name/stack/status only */
	#area-pane .poker-seat:not(.seat-south) .seat-cards { display: none; }

	/* Community board + pot: full-width middle row (forces the wrap) */
	#area-pane .poker-board {
		position: static;
		transform: none;
		order: 2;
		flex: 1 1 100%;
		margin: 4px 0;
	}
	#area-pane .poker-board-cards { flex-wrap: wrap; justify-content: center; }

	/* Viewer's seat + hand: full-width bottom row */
	#area-pane .seat-south {
		order: 3;
		flex: 1 1 100%;
		padding-top: 4px;
		border-top: 1px solid rgba(255, 255, 255, 0.1);
	}

	/* Shrink cards so five community cards fit a phone width */
	#area-pane .poker-card-wrap { width: 42px; height: 62px; }
	#area-pane .poker-card-front,
	#area-pane .poker-card-back { border-radius: 5px; }
	#area-pane .poker-card-front .rank-top { font-size: 11px; top: 3px; left: 4px; }
	#area-pane .poker-card-front .suit-center { font-size: 19px; }
	#area-pane .poker-card-front .rank-bot { font-size: 11px; bottom: 3px; right: 4px; }
	/* The viewer's own hole cards get a touch more size for readability */
	#area-pane .seat-viewer .poker-card-wrap { width: 50px; height: 74px; }
	#area-pane .seat-viewer .poker-card-front .rank-top,
	#area-pane .seat-viewer .poker-card-front .rank-bot { font-size: 13px; }
	#area-pane .seat-viewer .poker-card-front .suit-center { font-size: 24px; }

	/* Action bar: fewer, bigger, evenly-sized tap targets */
	#area-pane .poker-cmdbar { gap: 6px; padding: 8px 4px 4px; }
	#area-pane .poker-cmd {
		flex: 1 1 auto;
		min-width: 68px;
		font-size: 13px;
		padding: 9px 8px;
		text-align: center;
	}
}

/* Draggable confirmation dialog (server { type:'confirm' }) */
.confirm-window {
	position: fixed;
	top: 30%;
	left: 50%;
	transform: translateX(-50%);
	/* Above the Tablet OS overlay (z-index 9200) so corp/tablet prompts sit on top. */
	z-index: 9600;
	width: 360px;
	max-width: 94vw;
	background: var(--bg2);
	border: 1px solid var(--accent);
	box-shadow: 0 4px 32px rgba(0, 0, 0, 0.6);
	font-family: var(--font-mono);
}
.confirm-drag-handle {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: 10px 14px;
	border-bottom: 1px solid var(--border);
	cursor: grab;
	user-select: none;
}
.confirm-title {
	color: var(--accent);
	font-size: 11px;
	letter-spacing: 2px;
	text-transform: uppercase;
}
.confirm-x {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	width: 22px;
	height: 22px;
	cursor: pointer;
	font-size: 11px;
	line-height: 1;
	font-family: var(--font-mono);
}
.confirm-body { padding: 16px; }
.confirm-prompt {
	margin: 0 0 16px;
	color: var(--text);
	font-size: 13px;
	line-height: 1.5;
}
.confirm-actions {
	display: flex;
	justify-content: flex-end;
	gap: 10px;
}
.confirm-actions button {
	padding: 7px 14px;
	cursor: pointer;
	font-size: 12px;
	font-family: var(--font-mono);
	background: var(--bg3);
	border: 1px solid var(--border);
	color: var(--text-dim);
}
.confirm-actions .confirm-ok {
	background: var(--accent-dim);
	border-color: var(--accent);
	color: var(--text-bright);
}
.confirm-actions button:hover { border-color: var(--accent); }

/* Select dialog (showSelectDialog) — a scrollable column of pickable options. */
.confirm-select-list {
	display: flex;
	flex-direction: column;
	gap: 6px;
	max-height: 260px;
	overflow-y: auto;
	margin-bottom: 16px;
}
.confirm-select-opt {
	text-align: left;
	padding: 8px 12px;
	cursor: pointer;
	font-size: 13px;
	font-family: var(--font-mono);
	background: var(--bg3);
	border: 1px solid var(--border);
	color: var(--text);
}
.confirm-select-opt:hover { border-color: var(--accent); color: var(--text-bright); background: var(--accent-dim); }
.confirm-empty { margin: 0 0 16px; color: var(--text-dim); font-size: 13px; text-align: center; }

/* High-stakes confirm (sign-out warning) — danger banners top and bottom */
.confirm-danger { border-color: var(--red); box-shadow: 0 4px 32px rgba(255, 40, 40, 0.35); }
.confirm-danger .confirm-title { color: var(--red); }
.confirm-danger-banner {
	background: var(--red);
	color: #000;
	font-weight: bold;
	text-align: center;
	letter-spacing: 6px;
	font-size: 12px;
	padding: 5px 0;
	text-transform: uppercase;
}
.confirm-danger .confirm-actions .confirm-ok-danger {
	background: var(--red);
	border-color: var(--red);
	color: #000;
	font-weight: bold;
}

/* Arrest booking record popup (arrest_notice) — reuses the confirm-window frame */
.arrest-window { width: 440px; }
.arrest-banner {
	font-size: 12px; letter-spacing: 1px; color: var(--accent);
	text-align: center; margin-bottom: 14px; padding-bottom: 12px;
	border-bottom: 1px solid var(--border);
}
.arrest-stars { margin-left: 6px; letter-spacing: 2px; color: var(--accent); text-shadow: 0 0 8px var(--accent); }
.arrest-stars-empty { color: var(--accent-dim); text-shadow: none; }
.arrest-row { display: flex; gap: 12px; padding: 6px 0; font-size: 12px; line-height: 1.45; }
.arrest-label {
	flex: 0 0 84px; color: var(--text-dim); text-transform: uppercase;
	letter-spacing: 1px; font-size: 10px; padding-top: 2px;
}
.arrest-val { flex: 1; color: var(--text); }
.arrest-fine { color: var(--red); }
.arrest-debt { color: var(--red); font-weight: bold; }

/* Splice lab report — the A+…F grade card after a splice resolves. */
.splice-report { width: 300px; }
.splice-report .confirm-body { text-align: center; }
.sr-grade { font-family: var(--font-mono); font-size: 66px; font-weight: bold; line-height: 1; margin: 4px 0 2px; }
.sr-a { color: #39ff9e; text-shadow: 0 0 18px rgba(57, 255, 158, 0.55); }
.sr-b { color: #ffc24d; text-shadow: 0 0 14px rgba(255, 190, 60, 0.5); }
.sr-c { color: #ffb14d; }
.sr-d { color: #ff8a5a; }
.sr-f { color: var(--red); text-shadow: 0 0 14px rgba(255, 60, 60, 0.5); }
.sr-name { font-size: 13px; color: var(--text); letter-spacing: 1px; margin-bottom: 12px; }
.sr-row { display: flex; justify-content: space-between; font-size: 12px; padding: 5px 2px; border-top: 1px solid var(--border); }
.sr-row b { color: var(--accent); }
.sr-note { font-size: 11px; color: var(--text-dim); margin: 10px 2px 2px; line-height: 1.5; }
.sr-sealed { font-size: 10px; color: #5fd0e0; margin: 10px 2px 0; line-height: 1.5; }
.sr-sealed b { color: #8fe6f0; }

/* Apprehend prompt — the submit-or-run countdown when a cop grabs you (≤3.5★). */
.apprehend-window {
	position: fixed; left: 50%; top: 40%; transform: translate(-50%, -50%);
	z-index: 400; width: 340px; padding: 18px 20px 16px;
	background: rgba(14, 5, 5, 0.96); border: 1px solid var(--red); border-radius: 6px;
	box-shadow: 0 0 0 1px rgba(255, 60, 60, 0.25), 0 10px 40px rgba(0, 0, 0, 0.6), 0 0 40px rgba(255, 40, 40, 0.25);
	font-family: var(--font-mono); text-align: center;
	animation: apprehend-in 0.18s ease-out;
}
@keyframes apprehend-in {
	from { opacity: 0; transform: translate(-50%, -50%) scale(0.94); }
	to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
.apprehend-badge {
	font-size: 12px; letter-spacing: 3px; font-weight: bold; color: var(--red);
	text-shadow: 0 0 10px rgba(255, 50, 50, 0.7); margin-bottom: 8px;
	animation: apprehend-flash 1s steps(2) infinite;
}
@keyframes apprehend-flash { 0%, 100% { opacity: 1; } 50% { opacity: 0.6; } }
.apprehend-officer { font-size: 12px; color: var(--text); line-height: 1.5; margin-bottom: 14px; }
.apprehend-bar { height: 8px; background: rgba(255, 255, 255, 0.08); border-radius: 4px; overflow: hidden; margin-bottom: 16px; }
.apprehend-fill { height: 100%; width: 100%; background: linear-gradient(90deg, var(--red), #ffb14d); }
.apprehend-actions { display: flex; gap: 10px; }
.apprehend-actions button {
	flex: 1; padding: 9px 0; font-family: var(--font-mono); font-size: 12px; font-weight: bold;
	letter-spacing: 2px; border-radius: 4px; cursor: pointer; border: 1px solid;
}
.apprehend-submit { background: var(--bg3); color: var(--text); border-color: var(--border); }
.apprehend-submit:hover { border-color: var(--text-dim); }
.apprehend-run { background: #3a1410; color: #ff8a5a; border-color: #7a3016; }
.apprehend-run:hover { background: #4a1a12; }
.apprehend-hint { margin-top: 10px; font-size: 9px; letter-spacing: 1px; color: var(--text-dim); }
.apprehend-keys { color: var(--accent-dim); }

/* Conceal scan-sweep — palm contraband before the scanner line reaches it. */
.conceal-window {
	position: fixed; left: 50%; top: 42%; transform: translate(-50%, -50%);
	z-index: 400; padding: 18px 20px 22px;
	background: rgba(12, 8, 5, 0.97); border: 1px solid #7a5a16; border-radius: 6px;
	box-shadow: 0 0 0 1px rgba(255, 190, 60, 0.18), 0 10px 44px rgba(0, 0, 0, 0.65);
	font-family: var(--font-mono); text-align: center;
	animation: apprehend-in 0.18s ease-out;
}
.conceal-title { font-size: 12px; letter-spacing: 3px; font-weight: bold; color: #ffc24d; text-shadow: 0 0 10px rgba(255, 170, 50, 0.5); }
.conceal-sub { font-size: 11px; color: var(--text-dim); margin: 6px 0 16px; }
.conceal-sub b { color: var(--text); }
.conceal-keys { color: var(--accent-dim); }
.conceal-track {
	position: relative; height: 92px; margin: 0 auto;
	background: repeating-linear-gradient(90deg, transparent 0 28px, rgba(255,255,255,.03) 28px 29px);
	border: 1px solid var(--border); border-radius: 4px;
}
.conceal-line {
	position: absolute; top: -3px; bottom: -3px; left: 0; width: 2px;
	background: linear-gradient(180deg, transparent, #ff5a3c 20%, #ffb14d 80%, transparent);
	box-shadow: 0 0 12px 2px rgba(255, 90, 60, 0.7); pointer-events: none; z-index: 3;
}
.conceal-chip {
	position: absolute; top: 12px; width: 66px; height: 68px; cursor: pointer;
	display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
	background: var(--bg3); border: 1px solid var(--border); border-radius: 5px;
	transition: transform .12s, border-color .12s, opacity .2s, background .2s;
}
.conceal-chip:hover { border-color: #7a5a16; }
.conceal-glyph { font-size: 20px; color: #ffc24d; line-height: 1; }
.conceal-name { font-size: 8px; letter-spacing: .5px; color: var(--text-dim); max-width: 60px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.conceal-tag { font-size: 8px; letter-spacing: 1px; color: var(--accent-dim); font-weight: bold; }
.conceal-chip.conceal-palmed { border-color: #39c27a; background: #0f2418; transform: translateY(-8px); cursor: default; }
.conceal-chip.conceal-palmed .conceal-tag { color: #39ff9e; }
.conceal-chip.conceal-botch { border-color: var(--red); background: #2a0e0c; cursor: default; }
.conceal-chip.conceal-botch .conceal-tag { color: var(--red); }
.conceal-chip.conceal-scanned { opacity: 0.32; cursor: default; }
.conceal-chip.conceal-full { animation: conceal-shake 0.2s; }
@keyframes conceal-shake { 0%,100% { transform: translateX(0); } 25% { transform: translateX(-4px); } 75% { transform: translateX(4px); } }
.confirm-input {
	width: 100%;
	box-sizing: border-box;
	background: var(--bg3);
	border: 1px solid var(--border);
	color: var(--text);
	font-family: var(--font-mono);
	font-size: 14px;
	padding: 7px 10px;
	outline: none;
	border-radius: 2px;
	margin: 0 0 16px;
}
.confirm-input:focus { border-color: var(--accent); }
.poker-cmd.disabled {
	color: #8f9a91;
	background: rgba(255,255,255,0.03);
	border-color: rgba(255,255,255,0.1);
	cursor: default;
	opacity: 0.5;
	box-shadow: none;
}

/* ── Drug "trip" FX ─────────────────────────────────────────────────────────
   Driven by dispatch.js: a #trip-overlay div + the `tripping` body class, with
   two live-tunable custom properties the server pushes on come-up/peak/comedown:
     --trip-hue        base hue (0–360) from the drug's palette
     --trip-intensity  0–1, scales overlay opacity, blur and shake amplitude   */
:root { --trip-hue: 120; --trip-intensity: 0.6; }

#trip-overlay {
	position: fixed;
	inset: 0;
	z-index: 9997;
	pointer-events: none;
	mix-blend-mode: screen;
	background:
		radial-gradient(circle at 50% 40%,
			hsla(var(--trip-hue), 100%, 60%, calc(0.18 * var(--trip-intensity))),
			transparent 70%),
		linear-gradient(120deg,
			hsla(var(--trip-hue), 90%, 55%, calc(0.12 * var(--trip-intensity))),
			hsla(calc(var(--trip-hue) + 120), 90%, 55%, calc(0.10 * var(--trip-intensity))));
	animation: trip-drift 26s linear infinite;
}

/* Slow, smooth cycle through the whole colour wheel with a gentle opacity
   breathe — a pulse between colours, not a strobe. */
@keyframes trip-drift {
	0%   { filter: hue-rotate(0deg);   opacity: 0.5; transform: scale(1); }
	25%  { opacity: 0.72; }
	50%  { filter: hue-rotate(180deg); opacity: 0.62; transform: scale(1.03); }
	75%  { opacity: 0.72; }
	100% { filter: hue-rotate(360deg); opacity: 0.5; transform: scale(1); }
}

/* Whole game view slowly warps/breathes while tripping — a soft rolling
   distortion rather than a jitter. Amplitudes scale with intensity. */
body.tripping #main {
	animation: trip-warp-view calc(11s - 3s * var(--trip-intensity)) ease-in-out infinite;
	filter: saturate(calc(1 + 0.7 * var(--trip-intensity))) contrast(calc(1 + 0.12 * var(--trip-intensity)));
	will-change: transform, filter;
}

@keyframes trip-warp-view {
	0%   { transform: scale(1) skew(0deg, 0deg); }
	25%  { transform: scale(calc(1 + 0.012 * var(--trip-intensity))) skew(calc(0.6deg * var(--trip-intensity)), calc(-0.3deg * var(--trip-intensity))); filter: blur(calc(0.4px * var(--trip-intensity))); }
	50%  { transform: scale(calc(1 + 0.02 * var(--trip-intensity))) skew(0deg, calc(0.4deg * var(--trip-intensity))); }
	75%  { transform: scale(calc(1 + 0.012 * var(--trip-intensity))) skew(calc(-0.6deg * var(--trip-intensity)), calc(0.3deg * var(--trip-intensity))); filter: blur(calc(0.4px * var(--trip-intensity))); }
	100% { transform: scale(1) skew(0deg, 0deg); }
}

/* Log slowly breathes its colour along with the trip — a gentle hue pulse,
   no hard glitch flashes. */
body.tripping #output {
	animation: trip-hue-breathe 14s ease-in-out infinite;
}

@keyframes trip-hue-breathe {
	0%, 100% { filter: hue-rotate(0deg) saturate(1.1); }
	50%      { filter: hue-rotate(40deg) saturate(1.3) brightness(1.05); }
}

/* Trip event lines + [trip] melting text. */
.msg-trip { color: hsl(var(--trip-hue), 90%, 72%); font-style: italic; text-shadow: 0 0 6px hsla(var(--trip-hue), 100%, 60%, 0.5); }

.trip-text {
	display: inline-block;
	animation: trip-warp 3.6s ease-in-out infinite;
}

@keyframes trip-warp {
	0%   { transform: skewX(0deg) translateY(0); letter-spacing: 0; }
	33%  { transform: skewX(3deg) translateY(-1px); letter-spacing: 0.5px; }
	66%  { transform: skewX(-3deg) translateY(1px); letter-spacing: -0.3px; }
	100% { transform: skewX(0deg) translateY(0); letter-spacing: 0; }
}

@media (prefers-reduced-motion: reduce) {
	#trip-overlay, body.tripping #main, body.tripping #output, .trip-text { animation: none; }
	body.tripping #main { transform: none; }
}

/* ── Sanity "dread" FX ───────────────────────────────────────────────────────
   The scary cousin of the trip overlay. Driven by dispatch.js `sanity_fx`: a
   #sanity-overlay vignette + `unhinged`/`insane` body classes, scaled by one
   live property:
     --sanity-intensity  0–1, deepens the closing dark and the sick-red tint   */
:root { --sanity-intensity: 0; }

#sanity-overlay {
	position: fixed;
	inset: 0;
	z-index: 9996;
	pointer-events: none;
	/* Edges darken inward (multiply), with a faint sick-red bloom that grows as
	   sanity falls. The vignette closes in tighter at high intensity. */
	background:
		radial-gradient(ellipse at 50% 45%,
			transparent calc(62% - 32% * var(--sanity-intensity)),
			rgba(0, 0, 0, calc(0.55 * var(--sanity-intensity))) 100%),
		radial-gradient(circle at 50% 60%,
			rgba(70, 6, 10, calc(0.18 * var(--sanity-intensity))),
			transparent 75%);
	mix-blend-mode: multiply;
	animation: sanity-breathe calc(7s - 2.5s * var(--sanity-intensity)) ease-in-out infinite;
}

/* A slow, sick breathe — the walls closing in and easing back. */
@keyframes sanity-breathe {
	0%, 100% { opacity: calc(0.6 + 0.25 * var(--sanity-intensity)); transform: scale(1); }
	50%      { opacity: 1; transform: scale(calc(1 + 0.03 * var(--sanity-intensity))); }
}

/* Hallucination band: colour drains out of the world and it sways, uneasy. */
body.unhinged #main {
	animation: sanity-sway calc(13s - 4s * var(--sanity-intensity)) ease-in-out infinite;
	filter: saturate(calc(1 - 0.55 * var(--sanity-intensity))) contrast(calc(1 + 0.15 * var(--sanity-intensity))) brightness(calc(1 - 0.08 * var(--sanity-intensity)));
	will-change: transform, filter;
}

@keyframes sanity-sway {
	0%, 100% { transform: translateX(0) skewY(0deg); }
	25%      { transform: translateX(calc(-3px * var(--sanity-intensity))) skewY(calc(-0.35deg * var(--sanity-intensity))); }
	50%      { transform: translateX(0) skewY(0deg); filter: blur(calc(0.5px * var(--sanity-intensity))); }
	75%      { transform: translateX(calc(3px * var(--sanity-intensity))) skewY(calc(0.35deg * var(--sanity-intensity))); }
}

/* Full breakdown: near-monochrome, and the log itself shudders faintly. */
body.insane #main { filter: saturate(0.15) contrast(1.3) brightness(0.86); }
body.insane #output { animation: sanity-shudder 5.5s steps(1) infinite; }

@keyframes sanity-shudder {
	0%, 92%, 100% { transform: translate(0, 0); }
	94%  { transform: translate(-1px, 1px); }
	96%  { transform: translate(1px, -1px); }
	98%  { transform: translate(-1px, 0); }
}

/* Misperception whispers — cold, low, italic; not the rainbow trip text. */
.msg-dread {
	color: #9a6b6b;
	font-style: italic;
	opacity: 0.9;
	letter-spacing: 0.4px;
	text-shadow: 0 0 5px rgba(120, 10, 14, 0.45);
}

@media (prefers-reduced-motion: reduce) {
	#sanity-overlay, body.unhinged #main, body.insane #output { animation: none; }
	body.unhinged #main { transform: none; }
}

/* ============================================================
   Custom sidebar panels (player-defined)
   ============================================================ */

/* "＋ panel" add button — mirrors the restore control: only shown while the
   sidebar is unlocked (drag-mode / resize-mode set by sidebar-order.js). */
.sidebar-add-panel { display: none; align-items: center; height: 28px; box-sizing: border-box;
	background: none; border: 1px solid var(--border);
	color: var(--text-dim); font-size: 10px; padding: 0 8px; border-radius: 3px; cursor: pointer; }
.sidebar-add-panel:hover { color: var(--accent); border-color: var(--accent); }
#sidebar.drag-mode .sidebar-add-panel, #sidebar.resize-mode .sidebar-add-panel { display: inline-flex; }

.cpanel-body { font-size: 11px; }
.cpanel-title { display: flex; align-items: center; gap: 4px; }
.cpanel-controls { display: flex; align-items: center; gap: 2px; }
.cpanel-gear, .cpanel-remove { display: none; background: none; border: none; color: var(--text-dim);
	font-size: 11px; cursor: pointer; padding: 0 2px; }
.cpanel-gear:hover { color: var(--accent); }
.cpanel-remove:hover { color: var(--red); }
/* Config/remove controls only appear in edit mode, like the collapse buttons. */
#sidebar.drag-mode .cpanel-gear, #sidebar.resize-mode .cpanel-gear,
#sidebar.drag-mode .cpanel-remove, #sidebar.resize-mode .cpanel-remove { display: inline-block; }
.cpanel-empty { color: var(--text-dim); font-size: 11px; font-style: italic; }

/* Row list — shared by labeled-value / list widgets and skills */
.cpanel-rows { display: flex; flex-direction: column; gap: 2px; }
.cpanel-row { display: flex; justify-content: space-between; gap: 8px; }
.cpanel-row-label { color: var(--text-dim); }
.cpanel-row-val { color: var(--text); font-variant-numeric: tabular-nums; }
.cpanel-subhead { font-size: 9px; letter-spacing: 1px; color: var(--text-dim); text-transform: uppercase; margin: 6px 0 2px; }
.cpanel-skill-group:first-child .cpanel-subhead { margin-top: 0; }
.cpanel-text { color: var(--text); }

/* Sticky cam feed */
.cpanel-cam { border: 1px solid var(--border); border-radius: 2px; margin-bottom: 6px; overflow: hidden; }
.cpanel-cam-head { display: flex; justify-content: space-between; font-size: 10px; padding: 2px 4px;
	background: var(--bg3); color: var(--text-dim); }
.cpanel-cam-status { text-transform: uppercase; letter-spacing: 1px; }
.cpanel-cam-frame { font-family: var(--font-mono); font-size: 10px; line-height: 1.3; padding: 4px;
	white-space: pre-wrap; color: var(--text); }
.cpanel-cam-offline .cpanel-cam-frame, .cpanel-cam-damaged .cpanel-cam-frame,
.cpanel-cam-jammed .cpanel-cam-frame { color: var(--text-dim); }
.cpanel-cam-offline .cpanel-cam-status, .cpanel-cam-damaged .cpanel-cam-status,
.cpanel-cam-jammed .cpanel-cam-status { color: var(--red); }

/* Notes scratchpad */
.cpanel-notes { width: 100%; min-height: 70px; resize: vertical; background: var(--bg3);
	border: 1px solid var(--border); border-radius: 2px; color: var(--text); font-family: var(--font-mono);
	font-size: 11px; padding: 4px; box-sizing: border-box; }
.cpanel-notes:focus { outline: none; border-color: var(--accent); }

/* Wanted-level readout panel */
.cpanel-wanted { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; padding: 2px 0; }
.cpanel-wanted-stars { font-size: 32px; letter-spacing: 1px; line-height: 1; white-space: nowrap; color: var(--accent); text-shadow: 0 0 6px var(--accent); }
.cpanel-wanted-empty { color: var(--accent-dim); text-shadow: none; }
.cpanel-wanted-clean .cpanel-wanted-stars { color: var(--text-dim); text-shadow: none; }
.cpanel-wanted-note { font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 1px; }

/* Wanted-level HUD — top-centre neon pill (wanted.js). Hidden until `.active`;
   the flame canvas burns behind the stars, `.wanted-pulse` stings on a rise. */
#wanted-hud {
	position: fixed; top: 12px; left: 50%; transform: translateX(-50%);
	z-index: 150; display: none; align-items: center; gap: 8px;
	padding: 4px 16px; border-radius: 999px; pointer-events: none; overflow: visible;
	background: rgba(6, 2, 8, .72);
	border: 1px solid var(--accent);
	box-shadow: 0 0 14px var(--accent), inset 0 0 8px var(--accent-dim);
}
#wanted-hud.active { display: inline-flex; }
.wanted-label {
	position: relative; z-index: 2;
	font: 700 12px/1 var(--font-mono); letter-spacing: 3px;
	color: var(--accent); text-shadow: 0 0 6px var(--accent);
}
/* JS overrides `left` to centre the canvas on the stars; keep it behind them. */
.wanted-flame {
	position: absolute; left: 50%; bottom: 0; z-index: 1;
	width: 220px; height: 82px; transform: translateX(-50%); pointer-events: none;
}
.wanted-stars {
	position: relative; z-index: 2;
	font-size: 20px; line-height: 1; letter-spacing: 2px; white-space: nowrap;
	color: var(--accent); text-shadow: 0 0 8px var(--accent);
}
.wanted-empty { color: var(--accent-dim); text-shadow: none; }
@keyframes wanted-pulse {
	0%   { transform: translateX(-50%) scale(1);
	       box-shadow: 0 0 14px var(--accent), inset 0 0 8px var(--accent-dim); }
	30%  { transform: translateX(-50%) scale(1.09);
	       box-shadow: 0 0 28px var(--accent), inset 0 0 14px var(--accent); }
	100% { transform: translateX(-50%) scale(1);
	       box-shadow: 0 0 14px var(--accent), inset 0 0 8px var(--accent-dim); }
}
#wanted-hud.wanted-pulse { animation: wanted-pulse .5s ease-out; }

/* Builder modal */
.cpanel-modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 1000;
	display: flex; align-items: center; justify-content: center; }
.cpanel-modal { background: var(--bg2); border: 1px solid var(--border); border-radius: 4px;
	width: min(440px, 92vw); max-height: 84vh; display: flex; flex-direction: column; }
.cpanel-modal-head { display: flex; justify-content: space-between; align-items: center; padding: 10px 12px;
	border-bottom: 1px solid var(--border); font-size: 13px; color: var(--text); }
.cpanel-modal-x { background: none; border: none; color: var(--text-dim); font-size: 14px; cursor: pointer; }
.cpanel-modal-x:hover { color: var(--red); }
.cpanel-modal-body { padding: 12px; overflow-y: auto; }
.cpanel-modal-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 10px 12px;
	border-top: 1px solid var(--border); }
.cpanel-modal-save { background: var(--accent); color: var(--accent-ink); border: none; border-radius: 2px;
	padding: 5px 14px; cursor: pointer; font-size: 12px; }
.cpanel-modal-cancel { background: none; border: 1px solid var(--border); color: var(--text-dim);
	border-radius: 2px; padding: 5px 12px; cursor: pointer; font-size: 12px; }

/* Catalog grid */
.cpanel-catalog { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.cpanel-catalog-item { display: flex; flex-direction: column; gap: 3px; text-align: left;
	background: var(--bg3); border: 1px solid var(--border); border-radius: 3px; padding: 10px; cursor: pointer; }
.cpanel-catalog-item:hover { border-color: var(--accent); }
.cpanel-catalog-icon { font-size: 18px; }
.cpanel-catalog-title { color: var(--text); font-size: 12px; }
.cpanel-catalog-desc { color: var(--text-dim); font-size: 10px; }

/* Config form */
.cpanel-form { display: flex; flex-direction: column; gap: 12px; }
.cpanel-field { display: flex; flex-direction: column; gap: 4px; }
.cpanel-field-label { font-size: 10px; letter-spacing: 1px; text-transform: uppercase; color: var(--text-dim); }
.cpanel-input { background: var(--bg3); border: 1px solid var(--border); border-radius: 2px; color: var(--text);
	font-family: var(--font-mono); font-size: 12px; padding: 5px 6px; }
.cpanel-input:focus { outline: none; border-color: var(--accent); }
.cpanel-fieldpick { display: flex; flex-direction: column; gap: 8px; }
.cpanel-fieldgroup-label { font-size: 9px; letter-spacing: 1px; text-transform: uppercase; color: var(--text-dim); margin-bottom: 3px; }
.cpanel-fieldgroup, .cpanel-widgetpick { display: flex; flex-wrap: wrap; gap: 4px; }
.cpanel-fieldgroup { align-items: flex-start; }
.cpanel-chip { background: var(--bg3); border: 1px solid var(--border); border-radius: 10px; color: var(--text-dim);
	font-size: 11px; padding: 3px 9px; cursor: pointer; }
.cpanel-chip:hover { border-color: var(--accent); }
.cpanel-chip.on { background: var(--accent); color: #fff; border-color: var(--accent); }

/* Accordion panels — click header to expand, pushing others aside */
.cpanel-head { display: flex; align-items: center; gap: 4px; min-width: 0; }
.cpanel-caret { color: var(--text-dim); font-size: 10px; flex-shrink: 0; }
.cpanel-accordion > .sidebar-label { cursor: pointer; }
.cpanel-accordion > .sidebar-label:hover .cpanel-title { color: var(--accent); }
/* Expanded accordion fills freed space and scrolls internally (the sidebar itself never scrolls). */
.cpanel-accordion:not(.collapsed) { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.cpanel-accordion:not(.collapsed) .cpanel-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; }

/* Universal option checkbox + hint text in the config form */
.cpanel-check { display: flex; align-items: center; gap: 8px; font-size: 11px; color: var(--text-dim); cursor: pointer; }
.cpanel-check input { accent-color: var(--accent); flex-shrink: 0; }
.cpanel-field-label em { color: var(--text-dim); font-style: italic; text-transform: none; letter-spacing: 0; }

/* Surveillance-camera crime catch: a red vignette flash + the in-room callout line. */
#camera-flash-overlay {
	position: fixed; inset: 0; pointer-events: none; z-index: 9999; opacity: 0;
	background: radial-gradient(circle, rgba(255,30,40,0) 45%, rgba(255,30,40,0.4) 100%);
}
#camera-flash-overlay.flash { animation: camera-flash-anim 0.7s ease-out; }
@keyframes camera-flash-anim { 0% { opacity: 0; } 15% { opacity: 1; } 100% { opacity: 0; } }
.camera-alert { color: var(--red); font-weight: bold; }

/* Crime charged nearby: a soft, noticeable double-pulse red glow — gentler and
   quieter than the camera-catch flash above, and unlike the ESP lockdown it
   doesn't loop. Paired with a faster-pitched siren one-shot (see crime_alert
   in dispatch.js / SFX_CRIME_ALERT server-side). */
#crime-alert-overlay {
	position: fixed; inset: 0; pointer-events: none; z-index: 9998; opacity: 0;
	background: radial-gradient(circle, rgba(255,40,40,0) 55%, rgba(255,30,30,0.22) 100%);
}
#crime-alert-overlay.flash { animation: crime-alert-anim 1.6s ease-in-out; }
@keyframes crime-alert-anim {
	0%, 100% { opacity: 0; }
	20%       { opacity: 0.55; }
	50%       { opacity: 0.15; }
	70%       { opacity: 0.4; }
}

/* ── Reorderable lists (list-reorder.js) ─────────────────────────────────
   Per-panel edit control (⇅ toggle + ⟲ reset) lets a player drag list rows to
   reorder them and ✕ to remove them; state persists per list in localStorage. */
.sidebar-label-controls { display: inline-flex; align-items: center; }
.list-edit-controls { display: inline-flex; align-items: center; gap: 2px; margin-left: auto; }
.list-edit-toggle, .list-edit-reset {
	background: none; border: none; color: var(--text-dim);
	font-size: 12px; line-height: 1; padding: 0 3px; cursor: pointer;
}
.list-edit-toggle:hover, .list-edit-reset:hover { color: var(--accent); }
.list-edit-toggle.active { color: var(--accent); }
/* Reset only appears once editing is engaged. */
.list-edit-reset { display: none; }
.list-edit-controls.editing .list-edit-reset { display: inline-block; }

/* A hidden (deleted) row — hidden via class so app logic keeps owning any inline
   display it uses for its own conditional visibility (e.g. radiation bar). */
.list-row-hidden { display: none !important; }

/* Row-level affordances only show while the owning list is in edit mode. */
.list-row { position: relative; }
.list-row-del {
	display: none; position: absolute; top: 2px; right: 2px; z-index: 2;
	background: none; border: none; color: var(--text-dim);
	font-size: 11px; line-height: 1; padding: 0 2px; cursor: pointer;
}
.list-row-del:hover { color: var(--red); }
.list-editing .list-row-del { display: inline-block; }
.list-editing > .list-row {
	cursor: grab;
	outline: 1px dashed var(--border, rgba(255,255,255,0.15));
	outline-offset: -1px;
}
.list-editing > .list-row-dragging { opacity: 0.45; cursor: grabbing; }

/* ── Two-panel trade window (trade plugin) ─────────────────────────────────── */
#trade-overlay {
	position: fixed;
	inset: 0;
	z-index: 9000;
	display: none;
	align-items: center;
	justify-content: center;
	background: rgba(0, 3, 8, 0.72);
	backdrop-filter: blur(2px);
}
#trade-box {
	width: min(720px, 96vw);
	max-height: 90vh;
	overflow-y: auto;
	background: var(--bg2);
	border: 1px solid var(--accent);
	border-radius: 4px;
	box-shadow: 0 8px 40px rgba(0, 0, 0, 0.6);
	font-family: var(--font-mono);
	padding: 14px 16px 16px;
}
.trade-title {
	font-size: 13px;
	letter-spacing: 1px;
	color: var(--text-bright);
	border-bottom: 1px solid var(--border);
	padding-bottom: 8px;
	margin-bottom: 10px;
}
.trade-cols { display: flex; gap: 12px; }
.trade-col {
	flex: 1;
	min-width: 0;
	border: 1px solid var(--border);
	border-radius: 3px;
	padding: 8px 10px;
	background: var(--bg);
}
.trade-col h4 {
	margin: 0 0 6px;
	font-size: 11px;
	letter-spacing: 0.5px;
	color: var(--text-dim);
}
.trade-item {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 6px;
	padding: 2px 0;
	font-size: 13px;
}
.trade-empty { font-size: 12px; color: var(--text-dim); font-style: italic; padding: 2px 0; }
.trade-ready {
	margin-top: 6px;
	font-size: 11px;
	color: var(--text-dim);
	border-top: 1px dashed var(--border);
	padding-top: 4px;
}
.trade-ready.on { color: var(--green, #5fbf5f); }
.trade-credits-row { display: flex; gap: 6px; margin-top: 10px; }
.trade-inv {
	margin-top: 10px;
	border-top: 1px solid var(--border);
	padding-top: 8px;
	max-height: 210px;
	overflow-y: auto;
}
.trade-inv h4 { margin: 0 0 6px; font-size: 11px; letter-spacing: 0.5px; color: var(--text-dim); }
.trade-inv-row { display: flex; align-items: center; gap: 6px; padding: 2px 0; font-size: 13px; }
.trade-inv-row > span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trade-foot { display: flex; justify-content: space-between; gap: 8px; margin-top: 12px; }
input.trade-qty, #trade-credits-input {
	background: var(--bg3);
	border: 1px solid var(--border);
	color: var(--text);
	border-radius: 3px;
	padding: 2px 6px;
	font-family: var(--font-mono);
	font-size: 12px;
}
input.trade-qty { width: 56px; }
#trade-credits-input { flex: 1; }
.trade-btn {
	background: var(--bg3);
	border: 1px solid var(--border);
	color: var(--text);
	cursor: pointer;
	font-size: 12px;
	padding: 3px 10px;
	border-radius: 3px;
	font-family: var(--font-mono);
}
.trade-btn:hover { border-color: var(--accent); color: var(--text-bright); }
.trade-btn.on { border-color: var(--green, #5fbf5f); color: var(--green, #5fbf5f); }
.trade-btn.trade-cancel:hover { border-color: var(--red); color: var(--red); }

/* ── Voidwalking muster overlay ──────────────────────────────────────────────
   The last screen before the map ends. Dressed as the VOIDLINK firmware rather
   than ArchitectOS (out past the edge the grid doesn't reach you) — cold slate,
   scanline haze, survey-bracket corners, and a title that sits under a hairline
   rule stamped with the terms of the walk. */
.vwstage-overlay {
	position: fixed; inset: 0; z-index: 9000;
	display: flex; align-items: center; justify-content: center;
	background: radial-gradient(ellipse at 50% 35%, rgba(10,14,20,0.80), rgba(2,3,6,0.97));
	backdrop-filter: blur(3px);
	font-family: var(--font-mono);
}
/* A slow drifting interference band over the whole overlay — the void's own weather. */
.vwstage-overlay::after {
	content: ''; position: absolute; inset: 0; pointer-events: none; z-index: 1;
	background: repeating-linear-gradient(0deg, rgba(255,255,255,0.022) 0 1px, transparent 1px 3px);
}
.vwstage-panel {
	position: relative; z-index: 2;
	width: min(640px, 94vw); max-height: 92vh; overflow-y: auto;
	background:
		linear-gradient(180deg, rgba(40,229,255,0.05), transparent 22%),
		linear-gradient(160deg, var(--bg2), var(--bg));
	border: 1px solid var(--border);
	border-radius: 4px;
	box-shadow: 0 0 0 1px rgba(40,229,255,0.10), 0 22px 70px rgba(0,0,0,0.78), 0 0 60px rgba(40,229,255,0.06);
	color: var(--text);
	padding: 0 0 14px;
}
/* Survey brackets at the four corners — the instrument framing the void map uses. */
.vwstage-corner {
	position: absolute; width: 14px; height: 14px; pointer-events: none;
	border: 1px solid var(--cyan); opacity: 0.55;
}
.vwstage-corner.tl { top: 6px; left: 6px; border-right: 0; border-bottom: 0; }
.vwstage-corner.tr { top: 6px; right: 6px; border-left: 0; border-bottom: 0; }
.vwstage-corner.bl { bottom: 6px; left: 6px; border-right: 0; border-top: 0; }
.vwstage-corner.br { bottom: 6px; right: 6px; border-left: 0; border-top: 0; }
.vwstage-topbar {
	display: flex; justify-content: space-between; align-items: center;
	padding: 8px 14px; border-bottom: 1px solid var(--border);
	font-size: 0.64rem; letter-spacing: 3px; color: var(--text-dim);
	background: rgba(40,229,255,0.05);
}
.vwstage-os { color: var(--cyan); font-weight: 700; }
.vwstage-mode { color: var(--text-dim); }
.vwstage-head { padding: 18px 18px 4px; }
.vwstage-title {
	position: relative;
	font-size: 2.05rem; font-weight: 700; letter-spacing: 9px; color: var(--text-bright);
	text-transform: uppercase; line-height: 1.05;
	text-shadow: 0 0 26px rgba(40,229,255,0.35), 0 0 60px rgba(255,46,196,0.18);
}
/* Chromatic-split ghost of the wordmark — the signal isn't clean out here. */
.vwstage-title::before {
	content: attr(data-text); position: absolute; inset: 0;
	color: var(--accent); opacity: 0.35; transform: translate(-2px, 1px);
	pointer-events: none;
}
.vwstage-route {
	margin-top: 8px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
	font-size: 0.8rem; letter-spacing: 2px; text-transform: uppercase;
}
.vwstage-from { color: var(--text-dim); }
.vwstage-arrow { color: var(--accent); letter-spacing: 0; opacity: 0.75; }
.vwstage-to { color: var(--cyan); }
.vwstage-rule {
	margin-top: 12px; border-top: 1px solid var(--border); position: relative; height: 1px;
}
.vwstage-rule-tag {
	position: absolute; top: -0.55em; left: 0; padding-right: 10px;
	background: var(--bg2); color: var(--text-dim);
	font-size: 0.58rem; letter-spacing: 3px;
}
.vwstage-lore {
	margin: 14px 18px 10px; padding: 12px 14px;
	border-left: 2px solid var(--cyan); background: rgba(40,229,255,0.04);
	color: var(--text); font-size: 0.88rem; line-height: 1.6; font-style: italic;
}
.vwstage-cols { display: flex; gap: 12px; padding: 6px 18px; }
.vwstage-col { flex: 1; min-width: 0; }
.vwstage-colhead {
	font-size: 0.72rem; letter-spacing: 1.5px; text-transform: uppercase;
	color: var(--text-dim); border-bottom: 1px solid var(--border);
	padding-bottom: 5px; margin-bottom: 6px;
	display: flex; justify-content: space-between; align-items: baseline;
}
.vwstage-count { color: var(--cyan); font-size: 0.66rem; }
.vwstage-list { list-style: none; margin: 0; padding: 0; max-height: 190px; overflow-y: auto; }
.vwstage-list li {
	display: flex; align-items: center; gap: 6px;
	padding: 4px 2px; font-size: 0.8rem; border-bottom: 1px dotted rgba(42,42,64,0.5);
}
.vwstage-item { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vwstage-qty { color: var(--yellow); font-size: 0.72rem; }
.vwstage-empty { color: var(--text-dim); font-style: italic; }
.vwstage-party .vwstage-dot { color: var(--text-dim); width: 1em; }
.vwstage-party .vwstage-p-ready .vwstage-dot { color: var(--green); text-shadow: 0 0 8px rgba(57,255,143,0.6); }
.vwstage-party .vwstage-p-ready .vwstage-pstate { color: var(--green); }
.vwstage-who { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vwstage-tag {
	font-size: 0.6rem; letter-spacing: 1px; padding: 1px 5px; margin-left: 5px; border-radius: 6px;
	background: var(--bg3); color: var(--text-dim); text-transform: uppercase;
}
.vwstage-tag-you { background: var(--accent-dim); color: var(--text-bright); }
.vwstage-crown { margin-right: 5px; color: var(--yellow); font-size: 0.82em; text-shadow: 0 0 7px rgba(255,214,64,0.6); vertical-align: baseline; }
.vwstage-pstate { font-size: 0.66rem; letter-spacing: 1px; color: var(--text-dim); }
.vwstage-foot { display: flex; gap: 10px; padding: 14px 18px 4px; }
.vwstage-btn {
	flex: 1; padding: 11px; border-radius: 9px; border: 1px solid var(--border);
	font-family: var(--font-mono); font-size: 0.86rem; letter-spacing: 1px; cursor: pointer;
	background: var(--bg3); color: var(--text); transition: all 0.12s;
}
.vwstage-btn:hover { border-color: var(--cyan); }
.vwstage-btn-go { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); font-weight: 700; box-shadow: 0 0 20px rgba(255,46,196,0.35); }
.vwstage-btn-go:hover { filter: brightness(1.12); border-color: var(--accent); }
.vwstage-btn-done { background: rgba(57,255,143,0.12); color: var(--green); border-color: var(--green); cursor: default; }
.vwstage-btn-cancel { flex: 0 0 34%; color: var(--text-dim); }
.vwstage-hint { text-align: center; color: var(--text-dim); font-size: 0.7rem; letter-spacing: 0.5px; padding: 8px 18px 2px; }
.vwstage-chat { padding: 6px 18px 2px; display: flex; flex-direction: column; }
.vwstage-chat-log {
	height: 150px; overflow-y: auto; padding: 8px 10px; margin-bottom: 8px;
	border-radius: 8px; background: rgba(0,0,0,0.28); border: 1px solid var(--border);
}
.vwstage-chat-empty { color: var(--text-dim); font-style: italic; font-size: 0.78rem; }
.vwstage-chat-msg { padding: 3px 0; border-bottom: 1px dotted rgba(42,42,64,0.5); }
.vwstage-chat-msg:last-child { border-bottom: none; }
.vwstage-chat-from { display: block; font-size: 0.68rem; color: var(--cyan); margin-bottom: 1px; letter-spacing: 0.5px; }
.vwstage-chat-msg.me .vwstage-chat-from { color: var(--text-dim); font-style: italic; }
.vwstage-chat-text { color: var(--text); font-size: 0.8rem; word-break: break-word; }
.vwstage-chat-row { display: flex; gap: 8px; }
.vwstage-chat-input {
	flex: 1; min-width: 0; background: var(--bg); border: 1px solid var(--border); color: var(--text);
	border-radius: 8px; padding: 8px 10px; font-family: var(--font-mono); font-size: 0.8rem;
}
.vwstage-chat-input:focus { border-color: var(--cyan); outline: none; }
.vwstage-chat-input::placeholder { color: var(--text-dim); }
.vwstage-chat-send {
	flex: 0 0 auto; padding: 8px 14px; border-radius: 8px; border: 1px solid var(--border);
	background: var(--bg3); color: var(--text); font-family: var(--font-mono); font-size: 0.78rem; cursor: pointer;
}
.vwstage-chat-send:hover { border-color: var(--cyan); }

/* ── Accolades — unlock banner (plugins/accolades) ───────────────────────────
   A corner stack, not a centre-stage interrupt: entries are private and frequent
   enough that they must never sit in the reading path. Surfaces borrow the
   Tablet OS bevel language (accent-tinted --bg2 with highlight/shadow edges) but
   resolve through the GAME's theme tokens, so the banner follows every theme the
   player can pick without a second palette to maintain.
   Sparks are drawn on a sibling canvas (#accolade-fx) owned by accolades-banner.js. */
#accolade-stack {
	position: fixed; right: 16px; bottom: 16px; z-index: 9150;
	display: flex; flex-direction: column; align-items: flex-end; gap: 9px;
	width: min(342px, 42vw); pointer-events: none;
}
/* Pinned to the stack's corner, NOT the viewport — its pixel size is set by the
   JS (420x560 @ DPR<=1.5). A full-screen canvas meant clearing ~15 megapixels a
   frame for glints that only ever appear down here. */
#accolade-fx {
	position: fixed; right: 0; bottom: 0; z-index: 9151; pointer-events: none;
}

.accolade-card {
	position: relative; width: 100%; overflow: hidden;
	cursor: pointer; pointer-events: auto;
	padding: 12px 14px 11px 16px;
	font-family: var(--font-mono);
	background: linear-gradient(180deg,
		color-mix(in srgb, var(--accent) 18%, var(--bg2)),
		color-mix(in srgb, var(--accent) 6%, var(--bg2)));
	border: 1px solid var(--border);
	box-shadow:
		0 8px 26px rgba(0, 0, 0, 0.45),
		0 0 24px color-mix(in srgb, var(--accent) 17%, transparent),
		inset 0 1px 0 color-mix(in srgb, white 55%, transparent),
		inset 0 -3px 6px color-mix(in srgb, black 45%, transparent);
	animation: accolade-in 340ms cubic-bezier(0.16, 0.84, 0.44, 1) both;
}
.accolade-card.leaving { animation: accolade-out 520ms ease-out both; }
@keyframes accolade-in  { from { opacity: 0; transform: translateX(26px); } to { opacity: 1; transform: none; } }
/* Pure opacity — it fades where it stands rather than sliding away. A card that
   slides reads as dismissed; one that fades reads as finished. */
@keyframes accolade-out { to { opacity: 0; } }

/* accent rail, fills top→bottom on entry */
.accolade-card::before {
	content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 2px; z-index: 3;
	background: var(--accent); box-shadow: 0 0 10px var(--accent);
	transform-origin: top; animation: accolade-rail 420ms cubic-bezier(0.16, 0.84, 0.44, 1) both;
}
@keyframes accolade-rail { from { transform: scaleY(0); } to { transform: scaleY(1); } }

/* foil hairline across the top edge */
.accolade-card::after {
	content: ''; position: absolute; left: 0; right: 0; top: 0; height: 1px; z-index: 3;
	background: linear-gradient(90deg, transparent, var(--accent) 20%,
		color-mix(in srgb, white 70%, var(--accent)) 50%, var(--accent) 80%, transparent);
	opacity: 0.9; transform-origin: left;
	animation: accolade-wipe 520ms cubic-bezier(0.22, 0.9, 0.3, 1) both;
}
@keyframes accolade-wipe { from { transform: scaleX(0); } to { transform: scaleX(1); } }

.accolade-card .accolade-scan {
	position: absolute; inset: 0; pointer-events: none; z-index: 2;
	background: repeating-linear-gradient(0deg, rgba(0, 0, 0, 0.1) 0 1px, transparent 1px 3px);
}
.accolade-card .accolade-body { position: relative; z-index: 4; }

.accolade-kicker {
	font-size: 9.5px; letter-spacing: 3.2px; text-transform: uppercase;
	color: var(--text-dim); font-weight: bold; margin-bottom: 6px;
	display: flex; align-items: center; gap: 6px;
}
.accolade-kicker i { font-style: normal; color: var(--accent); font-size: 8px; }

/* The .intro-lore sheen, reused: accent body with a hot specular glint, dragged
   across twice by animating background-position, landing on solid ink. Same
   safety property as the original — the transparent fill exists ONLY inside the
   keyframes, so the title stays legible if animations are disabled. */
.accolade-title {
	display: block; font-size: 17.5px; font-weight: bold; letter-spacing: 0.3px; line-height: 1.24;
	background: linear-gradient(100deg,
		var(--accent) 0%, var(--accent) 30%,
		color-mix(in srgb, white 78%, var(--accent)) 42%,
		var(--text-bright) 50%,
		color-mix(in srgb, white 78%, var(--accent)) 58%,
		var(--accent) 70%, var(--accent) 100%);
	background-size: 300% 100%; background-position: 150% 0;
	-webkit-background-clip: text; background-clip: text;
	-webkit-text-fill-color: var(--text); color: var(--text);
	animation: accolade-sheen 3.6s ease-in-out 1 both;
}
@keyframes accolade-sheen {
	0%   { -webkit-text-fill-color: transparent; background-position: 150% 0; text-shadow: 0 0 10px var(--accent-dim); }
	40%  { -webkit-text-fill-color: transparent; background-position: -50% 0; text-shadow: 0 0 14px var(--accent); }
	42%  { -webkit-text-fill-color: transparent; background-position: 150% 0; text-shadow: 0 0 14px var(--accent); }
	80%  { -webkit-text-fill-color: transparent; background-position: -50% 0; text-shadow: 0 0 8px var(--accent-dim); }
	100% { -webkit-text-fill-color: var(--text);  background-position: -50% 0; text-shadow: none; }
}

/* Full --text, not --text-dim. The card surface is --bg2 tinted 6-18% with the
   accent, and dim ink on that was too low a delta to read at a glance — bold
   Courier made it worse, not better. --text-dim now only carries the footer meta,
   which is genuinely secondary. */
.accolade-line {
	margin-top: 5px; font-size: 13.5px; font-weight: bold;
	color: var(--text); line-height: 1.55;
}
.accolade-foot {
	margin-top: 9px; padding-top: 7px; border-top: 1px solid var(--border);
	display: flex; justify-content: space-between; align-items: center;
	font-size: 10.5px; letter-spacing: 1.2px; font-weight: bold;
}
.accolade-foot .accolade-xp { color: var(--accent); font-variant-numeric: tabular-nums; }
.accolade-foot .accolade-dismiss { color: var(--text-dim); opacity: 0; transition: opacity 140ms ease; }
.accolade-card:hover .accolade-dismiss { opacity: 1; }

/* Life bar — also the hover-pause tell. Duration comes from --accolade-hold, set
   on the card by accolades-banner.js from its own HOLD_MS, so the bar and the timer
   physically cannot drift apart. The fallback only applies if the JS never ran. */
.accolade-life {
	position: absolute; left: 0; bottom: 0; height: 2px; width: 100%; z-index: 3;
	transform-origin: left;
	background: color-mix(in srgb, var(--accent) 60%, transparent);
	animation: accolade-life var(--accolade-hold, 5s) linear both;
}
.accolade-card:hover .accolade-life { animation-play-state: paused; }
@keyframes accolade-life { from { transform: scaleX(1); } to { transform: scaleX(0); } }

@media (prefers-reduced-motion: reduce) {
	.accolade-card, .accolade-card::before, .accolade-card::after { animation-duration: 1ms; }
	.accolade-title { animation: none; -webkit-text-fill-color: var(--text); }
}

/* ── WARDROBE PANEL ────────────────────────────────────────────────────────
   Container furniture flagged `wardrobe` opens here instead of the box panel:
   saved outfits, a paper doll of drop pads, and the wardrobe's stock. Themed as
   a lacquered cabinet — warm brass over the usual terminal cyan, so it reads as
   a piece of furniture rather than another equipment list. */
#wardrobe-panel {
	display: none;
	position: fixed;
	inset: 0;
	background: transparent;
	z-index: 150;
	align-items: center;
	justify-content: center;
}
#wardrobe-panel.active { display: flex; }
#wardrobe-box {
	position: relative;
	background:
		linear-gradient(180deg, rgba(60, 40, 24, 0.22), rgba(0, 0, 0, 0) 60%),
		var(--bg2);
	border: 1px solid #b98b4a;
	box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6) inset, 0 18px 50px rgba(0, 0, 0, 0.55);
	padding: 20px;
	width: 96%;
	max-width: 900px;
	height: 82vh;
	max-height: 90vh;
	border-radius: 3px;
	display: flex;
	flex-direction: column;
	gap: 10px;
}
/* Centre seam of the double doors, standing open. */
#wardrobe-box::before {
	content: '';
	position: absolute;
	inset: 6px;
	border: 1px solid rgba(185, 139, 74, 0.22);
	border-radius: 2px;
	pointer-events: none;
}
#wardrobe-header {
	display: flex;
	justify-content: space-between;
	align-items: center;
}
#wardrobe-header h2 {
	color: #e0b978;
	font-size: 14px;
	letter-spacing: 3px;
	text-transform: uppercase;
	margin: 0;
}
#wardrobe-close {
	background: transparent;
	border: 1px solid var(--border);
	color: var(--text-dim);
	width: 26px;
	height: 26px;
	cursor: pointer;
	border-radius: 2px;
}
#wardrobe-close:hover { border-color: #e0b978; color: #e0b978; }
#wardrobe-notify {
	font-size: 11px;
	color: var(--yellow, #c8a000);
	text-align: center;
	min-height: 14px;
}
#wardrobe-body {
	display: flex;
	gap: 14px;
	flex: 1;
	min-height: 0;
	overflow: hidden;
}
.wdr-col {
	display: flex;
	flex-direction: column;
	gap: 6px;
	min-height: 0;
	min-width: 0;
}
#wardrobe-outfits-col { flex: 1.1; }
#wardrobe-doll-col { flex: 1.2; border-left: 1px solid rgba(185, 139, 74, 0.2); border-right: 1px solid rgba(185, 139, 74, 0.2); padding: 0 12px; }
#wardrobe-stock-col { flex: 1.1; }
#wardrobe-outfit-list,
#wardrobe-hanging-list,
#wardrobe-carried-list {
	flex: 1;
	overflow-y: auto;
	min-height: 60px;
	display: flex;
	flex-direction: column;
	gap: 6px;
	padding: 4px;
	border: 1px dashed transparent;
	border-radius: 3px;
}
.wdr-empty {
	font-size: 11px;
	color: var(--text-dim);
	opacity: 0.7;
	padding: 6px 4px;
	font-style: italic;
}

/* --- Saved outfits --- */
.wdr-outfit {
	border: 1px solid var(--border);
	border-left: 2px solid #b98b4a;
	border-radius: 2px;
	padding: 6px 8px;
	cursor: pointer;
	background: rgba(0, 0, 0, 0.2);
}
.wdr-outfit:hover { border-color: #e0b978; background: rgba(185, 139, 74, 0.08); }
.wdr-outfit-incomplete { border-left-color: var(--text-dim); }
.wdr-outfit-head {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 6px;
}
.wdr-outfit-name {
	color: #e0b978;
	font-size: 12px;
	letter-spacing: 1px;
	text-transform: uppercase;
}
.wdr-outfit-btns { display: flex; gap: 4px; flex-shrink: 0; }
.wdr-outfit-pieces {
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
	margin-top: 4px;
}
.wdr-piece {
	font-size: 10px;
	color: var(--text-dim);
	border: 1px solid var(--border);
	border-radius: 8px;
	padding: 0 6px;
}
/* A piece that is neither on you nor in this wardrobe — the outfit won't
   assemble fully, and the panel says so before you click wear. */
.wdr-piece-missing {
	color: var(--red, #ff5555);
	border-color: rgba(255, 85, 85, 0.4);
	text-decoration: line-through;
}

/* --- Paper doll --- */
#wardrobe-doll {
	position: relative;
	flex: 1;
	min-height: 0;
	display: grid;
	grid-template-columns: 1fr 1fr;
	grid-template-areas:
		"head  head"
		"torso hands"
		"legs  feet"
		"acc   acc";
	gap: 8px;
	align-content: start;
	padding: 8px 4px;
	overflow-y: auto;
}
/* The mannequin behind the pads: three blocks, deliberately crude — it reads as
   a tailor's dummy, not a character portrait. */
.wdr-figure {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	opacity: 0.09;
	pointer-events: none;
}
.wdr-fig-head { width: 34px; height: 34px; border-radius: 50%; background: #e0b978; }
.wdr-fig-torso { width: 66px; height: 84px; margin-top: 6px; border-radius: 10px 10px 4px 4px; background: #e0b978; }
.wdr-fig-legs { width: 46px; height: 78px; margin-top: 4px; border-radius: 4px 4px 8px 8px; background: #e0b978; }
.wdr-pad-head { grid-area: head; }
.wdr-pad-torso { grid-area: torso; }
.wdr-pad-hands { grid-area: hands; }
.wdr-pad-legs { grid-area: legs; }
.wdr-pad-feet { grid-area: feet; }
.wdr-pad-accessory { grid-area: acc; }
.wdr-pad {
	position: relative;
	border: 1px dashed var(--border);
	border-radius: 3px;
	padding: 5px 6px;
	min-height: 46px;
	display: flex;
	flex-direction: column;
	gap: 3px;
	background: rgba(0, 0, 0, 0.25);
}
.wdr-pad-label {
	font-size: 9px;
	letter-spacing: 1.5px;
	text-transform: uppercase;
	color: var(--text-dim);
}
.wdr-pad-items { display: flex; flex-wrap: wrap; gap: 4px; }
.wdr-pad-empty { color: var(--text-dim); opacity: 0.5; font-size: 11px; }
.wdr-worn {
	font-size: 10px;
	color: var(--text);
	background: rgba(185, 139, 74, 0.16);
	border: 1px solid rgba(185, 139, 74, 0.5);
	border-radius: 8px;
	padding: 0 6px;
	cursor: pointer;
}
.wdr-worn:hover { border-color: var(--red, #ff5555); color: var(--red, #ff5555); }
/* Lit while a garment that fits THIS slot is in flight. */
.wdr-pad-eligible { border-color: #e0b978; background: rgba(185, 139, 74, 0.1); }
.wdr-pad.ctr-drag-over { border-style: solid; border-color: var(--green); background: rgba(0, 255, 136, 0.07); }

#wardrobe-doll-actions {
	display: flex;
	gap: 6px;
	flex-shrink: 0;
}
#wardrobe-outfit-name {
	flex: 1;
	min-width: 0;
	background: rgba(0, 0, 0, 0.4);
	border: 1px solid var(--border);
	color: var(--text);
	font-family: inherit;
	font-size: 12px;
	padding: 4px 6px;
	border-radius: 2px;
}
#wardrobe-outfit-name:focus { outline: none; border-color: #e0b978; }

/* --- Stock lists --- */
.wdr-garment { border-left: 2px solid rgba(185, 139, 74, 0.55); }
.wdr-slot-tag {
	font-size: 9px;
	letter-spacing: 1px;
	text-transform: uppercase;
	color: var(--text-dim);
}
#wardrobe-footer {
	display: flex;
	justify-content: flex-end;
	flex-shrink: 0;
}

/* ── Interface tour (client/game/js/panels/tour.js) ───────────────────────────
   The one out-of-fiction onboarding surface: offered once to players new to
   text games, replayable with `tutorial`. A transparent cutout box carries a
   9999px outer shadow — that shadow IS the dimming, so whatever is spotlighted
   underneath stays perfectly legible — and the spotlighted region itself gets
   the same accent-glow grammar as .verb-teach / .point-ripple. */
#tour-overlay {
	position: fixed;
	inset: 0;
	z-index: 9700; /* above the confirm window (9600) and the Tablet OS (9200) */
	pointer-events: auto;
}
#tour-hole {
	position: absolute;
	border-radius: 6px;
	border: 1px solid var(--accent);
	box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.72), 0 0 18px 2px var(--accent-dim);
	transition: left 0.28s ease, top 0.28s ease, width 0.28s ease, height 0.28s ease;
	pointer-events: none;
}
/* The spotlighted region pulses once so the eye lands on it, not just off it. */
.tour-lit {
	animation: tour-lit-pulse 1.6s ease-in-out 2;
}
@keyframes tour-lit-pulse {
	0%, 100% { filter: none; }
	50%      { filter: brightness(1.25) saturate(1.15); }
}

.tour-card {
	position: fixed;
	z-index: 9750;
	width: 360px;
	max-width: 92vw;
	background: var(--bg2);
	border: 1px solid var(--accent);
	box-shadow: 0 4px 32px rgba(0, 0, 0, 0.7);
	font-family: var(--font-mono);
	padding: 12px 14px 10px;
	animation: dlg-in 0.18s ease-out;
}
/* The opening question is centred like a modal; tour steps are positioned in JS. */
.tour-offer {
	top: 28%;
	left: 50%;
	transform: translateX(-50%);
	z-index: 9760;
}
.tour-card-step {
	font-size: 10px;
	letter-spacing: 1px;
	color: var(--text-dim);
	margin-bottom: 4px;
}
.tour-card-title {
	color: var(--accent);
	font-weight: 700;
	letter-spacing: 0.5px;
	margin-bottom: 6px;
}
.tour-card-body {
	color: var(--text);
	font-size: 13px;
	line-height: 1.5;
}
.tour-card-body b { color: var(--text-bright); }
.tour-card-actions {
	display: flex;
	justify-content: flex-end;
	gap: 8px;
	margin-top: 12px;
}
.tour-btn {
	font-family: var(--font-mono);
	font-size: 12px;
	padding: 5px 12px;
	cursor: pointer;
	background: var(--accent);
	color: var(--bg);
	border: 1px solid var(--accent);
}
.tour-btn:hover { filter: brightness(1.12); }
.tour-btn-ghost {
	background: transparent;
	color: var(--text-dim);
	border-color: var(--border);
}
.tour-btn-ghost:hover { color: var(--text); border-color: var(--accent); }
.tour-btn-ghost[disabled] { opacity: 0.35; cursor: default; }

@media (max-width: 700px) {
	.tour-card { width: 300px; font-size: 12px; }
}
