/*
 * Mobile first. The layout starts as a single column with a bottom tab bar,
 * and only becomes multi-panel once there is room. Committed dark: a passive
 * tree is a starfield, and every comparable tool commits the same way.
 */

:root {
	--ground: #0b0a0c;
	--surface: #201d25;
	--surface-2: #2b2833;
	--line: #423d4c;
	--ink: #f4f1ea;
	--muted: #aba5b2;
	--accent: #d9a441;
	--accent-dim: #8a6a2c;
	--path: #6ba3d6;
	--danger: #c8624f;
	/* --danger is a fill colour, and at 4.21:1 on --surface it is below the
	   4.5:1 that body-size text needs. This is the same hue lifted for text. */
	--danger-text: #ef9d88;
	--total: #a865b8;
	/* Search highlight. Distinct from --path (route preview) and --accent
	   (allocated), because all three can be on screen at once. */
	--find: #5fd4c4;

	--font-display: Georgia, 'Iowan Old Style', 'Times New Roman', serif;
	--font-ui: system-ui, -apple-system, 'Segoe UI', sans-serif;
	--font-data: ui-monospace, 'SF Mono', 'Cascadia Mono', Consolas, monospace;

	--bar: 56px;
	--tabs: 58px;
	--strip: 46px;
	/* Reserved height for the mobile anchor: a 50px creative plus its frame.
	   Held even when empty, so a late-loading ad cannot shove the page. */
	--anchor: 62px;
}

* { box-sizing: border-box; }

html, body {
	margin: 0;
	height: 100%;
	overflow: hidden;
	background: var(--ground);
	color: var(--ink);
	font-family: var(--font-ui);
	font-size: 15px;
	line-height: 1.5;
	-webkit-text-size-adjust: 100%;
}

/* Touch targets never below 44px, and no accidental text selection while
   dragging the tree. */
button, select, input { font: inherit; color: inherit; }
button { -webkit-tap-highlight-color: transparent; touch-action: manipulation; }

/* DOM order is bar, tabs, panes, rail. On mobile the tabs belong at the bottom,
   so rows are assigned explicitly rather than left to source order. */
#app {
	display: grid;
	/* minmax(0, 1fr), not 1fr: a grid track sizes to its widest child by
	   default, so the horizontally scrolling top bar was stretching the whole
	   app past the viewport and pushing the last tab off screen. */
	grid-template-rows: var(--bar) minmax(0, 1fr) auto var(--anchor) var(--tabs);
	grid-template-columns: minmax(0, 1fr);
	height: 100dvh;
	width: 100%;
	max-width: 100%;
	overflow: hidden;
}
#bar, #panes, #tabs, #railPane { min-width: 0; }
#bar       { grid-row: 1; }
#panes     { grid-row: 2; }
#summary   { grid-row: 3; }
#adAnchor  { grid-row: 4; }
#tabs      { grid-row: 5; }

/* ---------- top bar ---------- */

#bar {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 0 12px;
	background: var(--surface);
	border-bottom: 1px solid var(--line);
	overflow-x: auto;
	scrollbar-width: none;
}
#bar::-webkit-scrollbar { display: none; }

/* The wordmark is the page's h1: a document with no level-one heading gives a
   screen reader nothing to start from. It is styled back down to the size the
   bar needs, since being the top heading is a structural fact and not a
   licence to be 32px. */
#bar .brand {
	margin: 0;
	font-family: var(--font-display);
	font-size: 16px;
	font-weight: 400;
	white-space: nowrap;
}
/* Below this there is no room for a wordmark next to the controls. */
/* Below this there is no room for a wordmark next to the controls, but it is
   the page's only h1: hiding it with display:none took the document's top-level
   heading away from screen readers on exactly the devices most likely to be
   using one. Hidden from the screen, kept in the tree. */
@media (max-width: 560px) {
	#bar .brand {
		position: absolute;
		width: 1px;
		height: 1px;
		margin: -1px;
		padding: 0;
		overflow: hidden;
		clip-path: inset(50%);
		white-space: nowrap;
	}
	#bar .ctl label, #bar .ctl .lbl { display: none; }
}
#bar .spacer { flex: 1; }
/* Class, Level and Hero Level read as one group and sit in the middle, with a
   flexible spacer either side, so the bar is balanced rather than left-heavy. */
.ctlgroup { display: flex; align-items: center; gap: 10px; flex: none; }

/* [hidden] is display:none in the UA sheet, which a class selector outranks.
   Without this the Hero Level tag stayed on screen showing a bare 0. */
.ctl[hidden] { display: none; }

.ctl {
	display: flex;
	align-items: center;
	gap: 6px;
	flex: none;
}
.ctl label, .ctl .lbl {
	font-size: 10px;
	letter-spacing: 0.09em;
	text-transform: uppercase;
	color: var(--muted);
}
.ctl select, .ctl input {
	background: var(--ground);
	border: 1px solid var(--line);
	border-radius: 6px;
	padding: 7px 8px;
	font-size: 14px;
	min-height: 38px;
}
.ctl input { width: 62px; font-family: var(--font-data); text-align: center; }
/* A border tint is not a focus indicator: at 1.6:1 against the resting border
   it is invisible to most people and absent for anyone who cannot see colour.
   The ring is the indicator; the tint stays as a second signal. */
.ctl select:focus, .ctl input:focus { border-color: var(--accent-dim); }
.ctl select:focus-visible, .ctl input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* A read-out, not a control: same footprint as the inputs beside it so the bar
   stays even, but flat rather than raised. */
.tag {
	display: inline-flex;
	align-items: center;
	min-height: 38px;
	padding: 0 11px;
	border: 1px solid var(--line);
	border-radius: 6px;
	background: var(--ground);
	font-family: var(--font-data);
	font-variant-numeric: tabular-nums;
	font-size: 14px;
	color: var(--accent);
}
.tag.spare { border-style: dashed; }

.btn {
	background: var(--surface-2);
	border: 1px solid var(--line);
	border-radius: 6px;
	padding: 8px 12px;
	font-size: 13px;
	min-height: 38px;
	cursor: pointer;
	white-space: nowrap;
}
.btn:hover { border-color: var(--accent-dim); }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn.done { border-color: var(--accent); color: var(--accent); }
/* The one that destroys something. --danger is a 4.21:1 fill on --surface, which
   is why the text uses --danger-text and only the border is the fill colour. */
.btn.danger { border-color: var(--danger); color: var(--danger-text); }
.btn.danger:hover { border-color: var(--danger-text); }
.btn.danger:focus-visible { outline-color: var(--danger-text); }

/* ---------- advertising ---------- */

/*
 * Reserved space, not live ads. Nothing here loads a network; these are boxes
 * of exactly the right size with the right aspect held, so that dropping a tag
 * in later cannot move a single pixel of the interface. Cumulative layout shift
 * is the usual way an ad-supported tool ruins itself, and it is entirely
 * avoidable by reserving first.
 *
 * They are marked aria-hidden and are not focusable: an empty placeholder has
 * nothing to announce to a screen reader.
 */
/* One switch. Without data-ads on the root every slot collapses and the
   reserved row gives its height back, so running the site ad-free costs
   nothing and needs no markup changes. */
:root:not([data-ads]) { --anchor: 0px; }
:root:not([data-ads]) .ad { display: none !important; }

.ad {
	display: grid;
	place-items: center;
	background: var(--surface);
	border: 1px dashed var(--line);
	border-radius: 6px;
	color: var(--muted);
	overflow: hidden;
}
.ad .adnote {
	font-family: var(--font-data);
	font-size: 10px;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	/* 0.7 put this at 4.10:1. Placeholder text is still text. */
	opacity: 0.85;
}
/* Filled slots drop the dashed hint. Set data-filled when a tag renders. */
.ad[data-filled] { border-style: solid; }
.ad[data-filled] .adnote { display: none; }

#adAnchor {
	margin: 0 8px 6px;
	height: calc(var(--anchor) - 6px);
	border-bottom: none;
}
/* The rail unit only exists where the rail does, and the pillar only once the
   viewport is wide enough that 160px costs the planner nothing. */
#adRail, #adPillar { display: none; }

/* In-content unit, at the foot of the pane below everything else. It started
   between two sections, which put a hole in the middle of the equipment list;
   at the bottom it is passed on the way out rather than read through. */
.ad.inline {
	width: 100%;
	max-width: 300px;
	height: 250px;
	margin: 28px auto 4px;
}
@media (min-width: 760px) {
	.ad.inline { max-width: 728px; height: 90px; }
}

/*
 * Sprites.
 *
 * Every icon in the app is a window onto an atlas. The box is whatever the
 * stylesheet says it is; the sprite is drawn on ::before and fits inside it.
 * Nothing in JS knows or needs to know the box size, which is what stops a
 * stylesheet change from silently mis-scaling an icon.
 */
.ico, .kmark { display: grid; place-items: center; overflow: hidden; }
.ico::before, .kmark::before {
	content: '';
	display: block;
	width: var(--w, 0);
	height: var(--h, 0);
	aspect-ratio: var(--sw, 1) / var(--sh, 1);
	max-width: 100%;
	max-height: 100%;
	background-image: var(--img, none);
	background-size: var(--bw, auto) var(--bh, auto);
	background-position: var(--bx, 0) var(--by, 0);
	background-repeat: no-repeat;
	image-rendering: pixelated;
}

/* ---------- panes ---------- */

#panes { position: relative; overflow: hidden; }
.pane {
	position: absolute;
	inset: 0;
	overflow-y: auto;
	overflow-x: hidden;
	overscroll-behavior: contain;
	-webkit-overflow-scrolling: touch;
	display: none;
}
.pane.on { display: block; }
/* Layouts inside a pane must respond to the pane, not the window. The summary
   rail takes 340px and the ad pillar another 184px, so a 1024px window leaves
   the pane 684px: a viewport media query said "wide" and the two-column gear
   layout overflowed under the rail. A container query measures the box that
   actually holds the content. */
.pane { container-type: inline-size; container-name: pane; }
.pane.flush { overflow: hidden; }

/* ---------- the two unhappy paths ----------

   Both of these elements are built in JS and neither exists in index.html, so
   their rules used to be inline `style.cssText` strings in main.js. That was a
   consequence of who owned which file rather than a decision, and it put layout
   for the failure states in the one place nobody looks when the layout of the
   failure states is wrong. They live here now, beside the pane they cover. */

/* A pane that could not be drawn. An overlay rather than a rewrite of the
   pane's contents: the tree pane owns a canvas, a search field and a zoom
   cluster that the tree module holds references to, and replacing its innerHTML
   would destroy all three along with the "Try again" being offered. */
.paneFail {
	position: absolute;
	inset: 0;
	z-index: 7;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 12px;
	padding: 24px;
	text-align: center;
	background: var(--ground);
}
.paneFail .failRow { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }

/* What a damaged share link cost. A strip at the foot of the pane area, not a
   dialog: the visitor did nothing wrong and has nothing to decide, so it takes
   no focus and blocks nothing. Pointer events are off on the strip and on again
   only for the dismiss button, because the pane underneath is what they came
   for and a banner that eats clicks is worse than no banner.

   Above .paneFail: if both are on screen the link damage is still worth
   reading, and that overlay's controls are centred well clear of this. */
#hashNotice {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 8;
	display: flex;
	justify-content: center;
	padding: 0 10px 10px;
	pointer-events: none;
}
#hashNotice .noticeBox {
	pointer-events: auto;
	display: flex;
	align-items: center;
	gap: 12px;
	max-width: 560px;
	padding: 8px 10px;
	border: 1px solid var(--line);
	border-radius: 8px;
	background: var(--surface);
	color: var(--ink);
	font-size: 12.5px;
	line-height: 1.45;
	box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
}

/* ---------- bottom tabs ---------- */

#tabs {
	display: grid;
	grid-template-columns: repeat(5, minmax(0, 1fr));
	background: var(--surface);
	border-top: 1px solid var(--line);
	padding-bottom: env(safe-area-inset-bottom);
}
#tabs button {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 2px;
	background: none;
	border: none;
	border-top: 2px solid transparent;
	color: var(--muted);
	font-size: 11px;
	letter-spacing: 0.04em;
	cursor: pointer;
	min-height: 44px;
}
#tabs button .ti { width: 21px; height: 21px; }
#tabs button.on { color: var(--accent); border-top-color: var(--accent); }

/* Four labels, one of which is "Incarnation", have to survive a 360px phone. */
/* Five labels across a 360px phone. Below this the words are dropped rather
   than truncated, since a clipped word reads worse than an icon alone. */
@media (max-width: 520px) {
	#tabs button { font-size: 9.5px; letter-spacing: 0; }
	#tabs button .ti { width: 19px; height: 19px; }
}
@media (max-width: 380px) {
	#tabs button { font-size: 0; gap: 0; }
	#tabs button .ti { width: 22px; height: 22px; }
}

/* ---------- shared bits ---------- */

.label {
	font-size: 10px;
	letter-spacing: 0.11em;
	text-transform: uppercase;
	color: var(--muted);
	margin: 0 0 8px;
}
/* Equipment, Relics and Charms ran together as one wall of cells. Every
   heading after the first opens a new section, with a rule to land the eye. */
.label + .slots { margin-bottom: 4px; }
.slots + .label,
.note + .label {
	margin-top: 26px;
	padding-top: 16px;
	border-top: 1px solid var(--line);
}
.empty { color: var(--muted); font-style: italic; font-size: 14px; }
.pad { padding: 16px 14px 28px; }

.row2 {
	display: flex;
	justify-content: space-between;
	gap: 12px;
	padding: 5px 0;
	font-size: 14px;
	border-bottom: 1px solid rgba(51, 46, 63, 0.5);
}
.row2:last-child { border-bottom: none; }
.row2 .vals { display: flex; gap: 8px; white-space: nowrap; }
.row2 b {
	font-family: var(--font-data);
	font-variant-numeric: tabular-nums;
	font-weight: 500;
	color: var(--accent);
}
.row2 b.pct { color: var(--path); }
.row2 b.tot { color: var(--total); }
.src { font-size: 11px; color: var(--muted); }

/* ---------- class trees ---------- */

.spec-head {
	display: flex;
	align-items: baseline;
	gap: 10px;
	margin: 4px 0 10px;
}
.spec-head h2 {
	margin: 0;
	font-family: var(--font-display);
	font-size: 19px;
	font-weight: 400;
}
.spec-head .count { font-family: var(--font-data); font-size: 12px; color: var(--muted); }

.tier-label {
	font-family: var(--font-data);
	font-size: 10px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--muted);
	padding: 12px 0 6px;
	border-top: 1px solid var(--line);
}
.tier-label.locked { color: var(--danger); }

.skills { display: grid; gap: 8px; grid-template-columns: minmax(0, 1fr); }

/* A prerequisite chain reads as one column: each skill sits below the one it
   needs, stepped in, with a rule down the left tying them together. */
.chain { display: grid; gap: 6px; margin-bottom: 14px; }
.chain .skill.sub { margin-left: calc(var(--d, 0) * 18px); position: relative; }
.chain .skill.sub::before {
	content: '';
	position: absolute;
	left: -11px; top: -9px; bottom: 50%;
	width: 10px;
	border-left: 1px solid var(--line);
	border-bottom: 1px solid var(--line);
	border-bottom-left-radius: 6px;
}
.skill .lv {
	margin-left: 7px;
	font-family: var(--font-data);
	font-size: 10px;
	color: var(--muted);
}
.rank.capped { color: var(--accent); }
.rank .bonus { margin-left: 5px; color: var(--path); }
.attr.capped .v { color: var(--accent); }
.attr button[disabled] { opacity: 0.35; cursor: not-allowed; }

.skill {
	display: flex;
	gap: 10px;
	align-items: center;
	padding: 8px 10px;
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: 8px;
	text-align: left;
	cursor: pointer;
	min-height: 56px;
}
.skill:hover { border-color: var(--accent-dim); }
.skill.taken { border-color: var(--accent); }
/* 0.45 faded the whole row to 4.14:1 on the name, 2.52:1 on the level and
   1.88:1 on the reason it is locked. Dimming a row is a legitimate way to say
   "not yet", but not at the price of the text that explains why. */
.skill[disabled] { opacity: 0.85; cursor: not-allowed; }
/* The row holds three controls, so the one that opens the detail is its own
   button rather than a handler on the row. Stripped back to inheriting
   everything, because visually it is still just the left of the row. */
.skinfo {
	display: flex;
	align-items: center;
	gap: 10px;
	flex: 1;
	min-width: 0;
	margin: 0;
	padding: 0;
	background: none;
	border: none;
	color: inherit;
	font: inherit;
	text-align: left;
	cursor: inherit;
}
.skinfo:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.skill .ico { width: 40px; height: 40px; flex: none; border-radius: 6px; }
.skill .body { flex: 1; min-width: 0; }
.skill .nm { font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.skill .rank { font-family: var(--font-data); font-size: 12px; color: var(--muted); }
.skill.taken .rank { color: var(--accent); }
/* --danger is 4.21:1 on this panel before the row is dimmed at all, so this
   line needs its own lighter red rather than a different opacity. */
.skill .why { font-size: 11px; color: var(--danger-text); }
.skill .step {
	flex: none;
	width: 34px; height: 34px;
	border-radius: 6px;
	background: var(--surface-2);
	border: 1px solid var(--line);
	font-size: 17px;
	line-height: 1;
	cursor: pointer;
}

/* ---------- incarnation board ---------- */

#board { display: block; width: 100%; height: 100%; touch-action: none; cursor: grab; }
#board:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; }
#board.drag { cursor: grabbing; }
#treeWrap { position: absolute; inset: 0; }

/* Search, floating over the top-left of the board. Deliberately opposite the
   zoom controls so neither ever sits under the other, at any width. */
#treeFind {
	position: absolute;
	inset: 10px auto auto 10px;
	z-index: 4;
	display: flex;
	align-items: center;
	gap: 4px;
	padding: 0 4px 0 9px;
	max-width: calc(100% - 20px);
	background: rgba(20, 19, 23, 0.94);
	border: 1px solid var(--line);
	border-radius: 9px;
	backdrop-filter: blur(3px);
}
#treeFind .fi { width: 15px; height: 15px; flex: none; color: var(--muted); }
#treeFind input {
	width: 190px;
	min-width: 0;
	padding: 9px 2px;
	background: none;
	border: none;
	font-size: 14px;
}
/* The blanket `outline: none` that used to sit in the rule above removed the
   only sign that Tab had landed here, and put nothing in its place. */
#treeFind input:focus { outline: none; }
#treeFind input:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
#treeFind input::placeholder { color: var(--muted); }
#treeFind input::-webkit-search-cancel-button { filter: invert(0.6); }
#findCount {
	flex: none;
	font-family: var(--font-data);
	font-variant-numeric: tabular-nums;
	font-size: 11px;
	color: var(--find);
	padding: 0 2px;
}
#findCount.none { color: var(--danger); }
#treeFind button {
	flex: none;
	width: 30px; height: 30px;
	border-radius: 6px;
	background: var(--surface-2);
	border: 1px solid var(--line);
	font-size: 13px;
	line-height: 1;
	cursor: pointer;
}
#treeFind button:disabled { opacity: 0.35; cursor: default; }
#treeFind button:not(:disabled):hover { border-color: var(--find); color: var(--find); }

/* On a phone the field takes the width it needs and the arrows stay reachable
   with a thumb. */
@media (max-width: 520px) {
	#treeFind { inset: 8px 8px auto 8px; }
	#treeFind input { width: 100%; flex: 1; }
}

/* The board switcher. Opposite the zoom controls, so neither covers the other
   at any width. */
#boardPick {
	position: absolute;
	inset: auto auto 10px 10px;
	z-index: 4;
	display: flex;
	gap: 4px;
	padding: 3px;
	background: rgba(20, 19, 23, 0.94);
	border: 1px solid var(--line);
	border-radius: 9px;
	backdrop-filter: blur(3px);
}
#boardPick button {
	padding: 7px 13px;
	background: none;
	border: 1px solid transparent;
	border-radius: 6px;
	color: var(--muted);
	font-size: 12.5px;
	cursor: pointer;
	white-space: nowrap;
}
#boardPick button.on { color: var(--accent); border-color: var(--accent-dim); background: var(--surface-2); }
#boardPick button:hover { color: var(--ink); }

#treeHud {
	position: absolute;
	inset: auto 10px 10px auto;
	display: flex;
	gap: 6px;
}
#treeHud button {
	width: 40px; height: 40px;
	border-radius: 8px;
	background: rgba(30, 27, 40, 0.9);
	border: 1px solid var(--line);
	font-family: var(--font-data);
	font-size: 16px;
	cursor: pointer;
}

#tip {
	position: fixed;
	z-index: 20;
	max-width: min(300px, 82vw);
	padding: 10px 12px;
	background: rgba(20, 18, 28, 0.97);
	border: 1px solid var(--accent-dim);
	border-radius: 6px;
	box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55);
	pointer-events: none;
	opacity: 0;
	transition: opacity 90ms ease;
}
#tip.on { opacity: 1; }
#tip h3 { margin: 0 0 5px; font-family: var(--font-display); font-size: 15px; font-weight: 400; }
#tip .tier { font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); margin-bottom: 6px; }
#tip .foot { margin-top: 8px; padding-top: 7px; border-top: 1px solid var(--line); font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; }
#tip .foot.take { color: var(--path); }
#tip .foot.drop { color: var(--danger); }
#tip .foot.far { color: var(--muted); }
/* Item tooltips reuse the tree's tip box. They can be long, so they scroll
   rather than running off the screen. */
#tip .row2 { font-size: 12.5px; padding: 3px 0; }
#tip .row2 .src em { color: var(--danger); font-style: normal; font-size: 10px; }
#tip p.desc { font-size: 12px; margin: 0 0 8px; color: var(--muted); }
#tip { max-height: min(70vh, 520px); overflow-y: auto; }

/* ---------- equipment ---------- */

.slots { display: grid; gap: 8px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* A slot and its sockets are one cell. Previously they were siblings of the
   grid, so a full-width socket strip landed under whichever row the slot
   happened to fall in rather than under the slot itself. */
.slotwrap { display: grid; gap: 6px; align-content: start; min-width: 0; }
.slot {
	display: flex;
	gap: 9px;
	align-items: center;
	padding: 8px;
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: 8px;
	text-align: left;
	cursor: pointer;
	min-height: 58px;
}
.slot.filled { border-color: var(--accent-dim); }
/* The slot carries role="button" so that it can be operated from the keyboard;
   that makes a focus ring mandatory rather than optional. */
.slot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cg.filled:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.slot .ico {
	width: 38px; height: 38px; flex: none;
	border-radius: 6px;
	background: var(--ground);
	border: 1px solid var(--line);
}


/* The client's slot art is a dark plate with a near-black silhouette, drawn to
   sit on the game's lighter inventory panel. On this ground it disappears, so
   it is lifted rather than dimmed; it still reads as a hint because a filled
   slot gets a full-strength icon and a lit border. */
.slot .ico.ph {
	background-color: transparent;
	filter: brightness(2.1) saturate(1.3);
	opacity: 0.8;
}
.slot:hover .ico.ph { opacity: 1; }
/* A grid panel, not a silhouette: it is already the right colour. */
.slot .ico.ph.plain { filter: none; opacity: 0.7; }
.slot .body { min-width: 0; flex: 1; }
.slot .sn { font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
.slot .it { font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.slot .it.none { color: var(--muted); font-style: italic; }

/* Stacked by default. Two columns once the pane is wide enough that the charm
   grid is not squeezing the gear slots. */
.geartop { display: grid; gap: 18px; }
.gcol { min-width: 0; }

@container pane (min-width: 700px) {
	/*
	 * A fixed track, not auto, and aligned to the top rather than stretched.
	 *
	 * Matching the two columns' heights sounded right and was wrong in use: the
	 * charm grid took its height from the gear column, so equipping anything
	 * resized the whole grid and every cell in it. Measured, adding a socketed
	 * weapon and chest took it from 142px wide to 167 and the cells from 45 to
	 * 53. A board that reflows while you are working on the thing beside it is
	 * worse than one that does not line up.
	 *
	 * So the width is fixed and the height follows from the 3:11 aspect. The
	 * grid now only changes size when the viewport does.
	 */
	.geartop {
		grid-template-columns: minmax(0, 1fr) 178px;
		align-items: start;
		gap: 22px;
	}
	/* The charm column takes its height from the gear column beside it. With a
	   fixed aspect ratio the width follows from the height, so the two columns
	   finish level whatever the gear column is currently doing: equipping a
	   socketed item makes it taller, and the charm grid grows to match. */
	.charmcol { min-width: 0; }
	.charmcol .charmgrid { width: 100%; max-width: none; }
	/* No rule above the charm heading: it starts a column, not a section. */
	.charmcol .label { border-top: none; margin-top: 0; padding-top: 0; }
}

/* The charm grid is a real grid, three wide and eleven tall, and a charm spans
   the rectangle it occupies. Square cells so a 2x2 reads as a square. */
.charmgrid {
	display: grid;
	grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
	/* minmax(0, 1fr), not 1fr: a bare 1fr keeps an automatic minimum, so a tile
	   whose contents are taller than its share grows its row and the grid loses
	   its shape. Selecting one large charm was enough to do it. */
	grid-auto-rows: minmax(0, 1fr);
	gap: 4px;
	max-width: 340px;
	aspect-ratio: var(--cols) / var(--rows);
	/* A backstop: nothing inside may make the grid bigger than its shape. */
	overflow: hidden;
}
.cg {
	display: grid;
	place-items: center;
	gap: 2px;
	min-width: 0;
	min-height: 0;
	background: var(--ground);
	border: 1px solid var(--line);
	border-radius: 6px;
	cursor: pointer;
	overflow: hidden;
}
.cg:hover { border-color: var(--accent-dim); }
.cg:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.cg.filled {
	grid-template-rows: minmax(0, 1fr) auto;
	background: var(--surface);
	border-color: var(--accent-dim);
	padding: 3px;
}
.cg.filled .ico { width: 100%; height: 100%; min-height: 0; }
/* The name only appears when the rectangle is big enough to hold it. */
.cg.filled .cn {
	font-size: 9px;
	line-height: 1.15;
	text-align: center;
	color: var(--muted);
	max-height: 2.3em;
	overflow: hidden;
}
.cg.locked {
	background: #0d0c0f;
	border-style: dashed;
	border-color: #322e3a;
	color: var(--muted);
	opacity: 0.85;
}
.cg.locked:hover { opacity: 1; border-color: var(--accent-dim); }
.cg.locked .lk { width: 13px; height: 13px; }

/* A charm that will not fit anywhere is shown, not hidden: knowing it exists
   and cannot be used is worth more than a shorter list. */
.pick.nofit { opacity: 0.45; cursor: not-allowed; }

/* Relics are smaller and more numerous than gear, so they get a denser grid. The charm grid keeps the game's three-wide shape at every width,
   because the three stat-gated cells sit at fixed positions in it. */
.slots.aux { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
.slots.aux.charms { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.slot.aux { min-height: 46px; padding: 6px; gap: 7px; }
.slot.aux .ico { width: 34px; height: 34px; }
.slot.aux .it { font-size: 12px; text-transform: capitalize; }
/* A locked cell is an invitation, not a dead end: the player clicks it to say
   their character has the quest or node that opens it. So it stays clickable
   and lifts on hover rather than reading as disabled. */
/* Recessed rather than faded: a darker ground than the panels around it reads
   as a hole in the grid, which is what a locked cell is. Dimming with opacity
   alone left it the same depth as its neighbours. */
.slot.aux.locked {
	background: #0d0c0f;
	border-style: dashed;
	border-color: #322e3a;
	opacity: 0.85;
	cursor: pointer;
}
/* Was #6f6a77, which measured 3.00:1 through the cell's own 0.85 fade. */
.slot.aux.locked .it { color: #9a94a3; }
.slot.aux.locked .ico { opacity: 0.4; }
.slot.aux.locked:hover {
	opacity: 1;
	border-color: var(--accent-dim);
	background: #131118;
}
.slot.aux.locked:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.slot .it .lk { width: 12px; height: 12px; vertical-align: -2px; margin-right: 3px; }

/* Shown only on an unlocked-but-empty gated cell. Filling the cell hides it,
   because re-locking a filled cell would silently discard the item. */
/* Only present on a gated cell that has been opened and is still empty. The
   cell itself is a button, so this one cannot sit inside it; it is a sibling
   pinned to the corner instead. */
.auxwrap { position: relative; display: grid; min-width: 0; }
.auxwrap .relock { position: absolute; top: 4px; right: 4px; }

.relock {
	flex: none;
	width: 24px; height: 24px;
	display: grid; place-items: center;
	padding: 0;
	background: none;
	border: 1px solid transparent;
	border-radius: 5px;
	color: var(--muted);
	cursor: pointer;
}
.relock .lk { width: 13px; height: 13px; }
.relock:hover { color: var(--ink); border-color: var(--line); }
.sockets {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 6px;
	margin: -2px 0 2px 8px;
}
.sockets .slot.aux { min-height: 38px; padding: 4px 7px 4px 4px; flex: 0 1 auto; }
.sockets .slot.aux .ico { width: 28px; height: 28px; }
.sockets .slot.aux .it { font-size: 11px; }
.sockset {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 3px 6px;
	border: 1px dashed var(--line);
	border-radius: 6px;
}
.sockset b { font-family: var(--font-data); min-width: 12px; text-align: center; }
.sockset .step { width: 26px; height: 26px; font-size: 15px; }

.rwbadge {
	font-size: 11px;
	letter-spacing: 0.04em;
	padding: 3px 9px;
	border-radius: 5px;
	border: 1px solid var(--accent);
	color: var(--accent);
	background: rgba(217, 164, 65, 0.08);
	white-space: nowrap;
}
.rwhint {
	font-size: 10px;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	padding: 4px 8px;
	border-radius: 5px;
	border: 1px dashed var(--line);
	background: none;
	color: var(--muted);
	cursor: pointer;
	white-space: nowrap;
}
.rwhint:hover { border-color: var(--accent-dim); color: var(--ink); }
.rwlist {
	margin: 2px 0 6px 8px;
	padding: 6px 12px;
	background: var(--ground);
	border: 1px solid var(--line);
	border-radius: 8px;
	max-height: 300px;
	overflow-y: auto;
}

.sockhint { font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }

.label .sub2 { color: var(--muted); font-weight: 400; text-transform: none; letter-spacing: 0; }

/* ---------- confirming something that cannot be taken back ----------

   Same idiom as the picker below, one step higher, because a question about
   throwing a build away can be asked while the picker is open. Created and
   removed by confirmAction rather than living in the markup: a dialog that is
   only ever seen twice should not be in the document the other 99% of the time,
   where it is one missing `display: none` away from covering the app. */
#confirm {
	position: fixed;
	inset: 0;
	z-index: 40;
	background: rgba(10, 9, 14, 0.85);
	display: grid;
	place-items: center;
	padding: 16px;
}
#confirmBox {
	width: min(420px, 100%);
	padding: 18px;
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: 12px;
	box-shadow: 0 12px 34px rgba(0, 0, 0, 0.55);
}
#confirmBox h2 {
	margin: 0 0 8px;
	font-family: var(--font-display);
	font-size: 18px;
	font-weight: 400;
}
#confirmBox p {
	margin: 0 0 16px;
	font-size: 13.5px;
	line-height: 1.5;
	color: var(--muted);
}
/* Cancel is first in the DOM, so it is where focus lands and where Tab starts,
   and it reads left to right in the order every other dialog uses. */
#confirmRow { display: flex; justify-content: flex-end; gap: 8px; }

#picker {
	position: fixed;
	inset: 0;
	z-index: 30;
	background: rgba(10, 9, 14, 0.85);
	display: none;
	padding: 12px;
}
#picker.on { display: grid; place-items: center; }
#pickerBox {
	width: min(560px, 100%);
	max-height: min(78dvh, 700px);
	display: grid;
	grid-template-rows: auto auto 1fr;
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: 12px;
	overflow: hidden;
}
#pickerHead { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--line); }
#pickerHead h3 { margin: 0; font-family: var(--font-display); font-size: 17px; font-weight: 400; flex: 1; }
#pickerSearch { margin: 10px 14px; padding: 9px 11px; background: var(--ground); border: 1px solid var(--line); border-radius: 7px; font-size: 15px; }
#pickerSearch:focus { border-color: var(--accent-dim); }
#pickerSearch:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }
#pickerList { overflow-y: auto; padding: 0 8px 12px; }
.pick {
	display: flex; gap: 10px; align-items: center; width: 100%;
	padding: 8px; background: none; border: none; border-radius: 8px;
	text-align: left; cursor: pointer; min-height: 52px;
}
.pick:hover { background: var(--surface-2); }
.pick .ico { width: 34px; height: 34px; flex: none; border-radius: 5px; background: var(--ground); }
.pick .pb { flex: 1; min-width: 0; }
.pick .nm { display: block; font-size: 14px; }
.pick .meta { display: block; font-size: 11px; color: var(--muted); }
/* What the item actually rolls, so a choice does not require equipping it. */
.pick .afx {
	display: block;
	font-family: var(--font-data);
	font-size: 10.5px;
	color: var(--path);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	margin-top: 1px;
}
/* Developer content: kept and reachable, but never mistaken for a real item. */
.devtag {
	margin-left: 7px;
	padding: 1px 5px;
	border-radius: 4px;
	border: 1px solid var(--danger);
	color: var(--danger);
	font-family: var(--font-data);
	font-size: 9.5px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	vertical-align: 1px;
}

.pick .sock {
	margin-left: 7px;
	color: var(--find);
	font-family: var(--font-data);
	font-size: 10.5px;
}
.pick .hit {
	margin-left: 7px;
	font-family: var(--font-data);
	font-size: 11px;
	color: var(--accent);
}
.pick { align-items: flex-start; }
.pick .ico { margin-top: 2px; }

/* ---------- detail / stats ---------- */

#detail h3 { margin: 0 0 2px; font-family: var(--font-display); font-size: 17px; font-weight: 400; text-wrap: balance; }
#detail .meta { font-family: var(--font-data); font-size: 11px; color: var(--muted); margin-bottom: 10px; }
#detail p.desc { font-size: 14px; margin: 0 0 12px; }

.build {
	font-family: var(--font-data);
	font-size: 10px;
	letter-spacing: 0.1em;
	color: var(--muted);
	opacity: 0.8;
}

.note {
	margin-top: 22px; padding-top: 12px;
	border-top: 1px solid var(--line);
	font-size: 11.5px; line-height: 1.45; color: var(--muted);
}

/* ---------- wider screens ---------- */

@media (min-width: 720px) {
	.skills { grid-template-columns: repeat(2, minmax(0, 1fr)); }
	.slots { grid-template-columns: repeat(3, minmax(0, 1fr)); }
	.pad { padding: 20px 20px 32px; }
}

/* Past this the tabs give way to a permanent stats rail, and the tree and the
   numbers can be read at the same time. */


/* ---------- always-visible build summary ---------- */

#summary {
	background: var(--surface);
	border-top: 1px solid var(--line);
	display: flex;
	flex-direction: column;
	min-height: 0;
}

#summaryToggle {
	display: flex;
	align-items: center;
	gap: 10px;
	width: 100%;
	min-height: var(--strip);
	padding: 0 14px;
	background: none;
	border: none;
	cursor: pointer;
	text-align: left;
}
#summaryToggle .chev {
	width: 18px; height: 18px;
	flex: none;
	color: var(--muted);
	transform: rotate(180deg);
	transition: transform 140ms ease;
}
#summaryToggle[aria-expanded="true"] .chev { transform: rotate(0deg); }

/* The strip carries the numbers you glance at without opening anything. */
#summaryStrip {
	flex: 1;
	min-width: 0;
	display: flex;
	gap: 14px;
	overflow-x: auto;
	scrollbar-width: none;
	font-family: var(--font-data);
	font-variant-numeric: tabular-nums;
	font-size: 12.5px;
	white-space: nowrap;
}
#summaryStrip::-webkit-scrollbar { display: none; }
#summaryStrip .k { color: var(--muted); }
#summaryStrip .v { color: var(--accent); }
#summaryStrip .none { color: var(--muted); font-family: var(--font-ui); font-style: italic; }
/* A stat the game data never named. Equipping one helmet used to fill this strip
   with "+587 +586 +585 +584" and no keys at all, because the key rendered empty;
   the id now stands in and is marked so it cannot be mistaken for a real name.
   Dotted rather than dimmed: this panel is always on screen and something the
   reader has to squint at is not a warning. See fallbackLabel in main.js. */
#summaryStrip .k.unnamed {
	border-bottom: 1px dotted var(--danger-text);
	color: var(--danger-text);
}

/* max-height alone only clips the panel: its contents keep their boxes, so a
   collapsed summary holds every one of its buttons in the tab order, invisible.
   That is fixed with `inert` in setSummaryOpen rather than with `visibility`
   here, because visibility also stops the panel being rendered at all and the
   totals are read out of it by more than one caller. */
#summaryBody {
	max-height: 0;
	overflow-y: auto;
	overscroll-behavior: contain;
	padding: 0 14px;
	transition: max-height 160ms ease, padding 160ms ease;
}
#summary.open #summaryBody {
	max-height: min(58dvh, 460px);
	padding: 2px 14px 18px;
}

/* Each stat shows where it came from, since the whole point is seeing how the
   four facets add up rather than just the total. */
.stat {
	padding: 6px 0;
	border-bottom: 1px solid rgba(51, 46, 63, 0.5);
}
.stat:last-child { border-bottom: none; }
.stat .head {
	display: flex;
	justify-content: space-between;
	gap: 12px;
	align-items: baseline;
	width: 100%;
	margin: 0;
	padding: 0;
	background: none;
	border: none;
	color: inherit;
	font: inherit;
	text-align: left;
}
/* 24px is the WCAG 2.2 target minimum, and the row measured 23. */
button.head { cursor: pointer; min-height: 24px; }
button.head:focus-visible { outline: 2px solid var(--path); outline-offset: 2px; }
.stat .head .nm { font-size: 13.5px; }
.stat .head .vals { display: flex; gap: 8px; white-space: nowrap; }
.stat b {
	font-family: var(--font-data);
	font-variant-numeric: tabular-nums;
	font-weight: 500;
	color: var(--accent);
}
.stat b.pct { color: var(--path); }
.stat b.tot { color: var(--total); }
.stat b.res { font-size: 15px; }
.stat b.res .cap {
	font-family: var(--font-ui);
	font-size: 9.5px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--danger);
	margin-left: 5px;
}
/* The breakdown's half of the unnamed-stat marker. --danger-text rather than
   --danger, which is a fill colour and sits at 4.21:1 on this panel. */
.stat .unk {
	font-size: 9.5px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--danger-text);
	margin-left: 6px;
}

/* ---------- grants ----------

   An item stat that holds a talent id is not a stat, and the summary used to
   present four of them as "Granted Aura 5 +587". Nothing below invents a new
   visual language for the corrected version: a grant is a .stat row with the
   same rule, spacing and facet pills as the numbers above it. The only thing it
   does differently is refuse to look like a quantity, which is the entire point.
   No signed value, no tabular figures, no accent gold. */
/* The grants sit above the numbers under their own heading. The rule separates
   two things that must not be read as one list, which is what went wrong: four
   talent ids were sorted in among the stats by magnitude and looked exactly like
   them. */
#summaryBody .label.sub {
	margin-top: 4px;
	padding-top: 10px;
	border-top: 1px solid var(--line);
}
.stat.grant + .stat:not(.grant) {
	margin-top: 10px;
	padding-top: 12px;
	border-top: 1px solid var(--line);
}
/* The name keeps the line and the level wraps under it, rather than the other
   way round. Without this "PROCS  level 12-18 · 7% on attack" took the first line
   and pushed "Live by the Sword" onto the second, which reads as though the row
   is about the numbers. It is about the skill. */
.stat.grant .head { align-items: baseline; flex-wrap: wrap; }
.stat.grant .nm { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 6px; flex: 1 1 auto; }
.stat.grant .vals { flex: 0 1 auto; }
/* "Grants aura" ahead of the talent's name, in the same key-and-value weighting
   the strip uses, so the row reads as a sentence at a glance. */
.stat.grant .gk {
	font-size: 9.5px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--muted);
}
/* The level and chance. --muted rather than --accent because --accent is what
   every real number on this panel is drawn in, and a level is a property of the
   grant rather than a total the build has earned. */
.stat.grant b.gv {
	font-weight: 400;
	font-size: 12px;
	color: var(--muted);
}
/* A count of sources, not a value, so the strip's grant chip stays the one thing
   in that row without a plus sign in front of it. */
#summaryStrip .v.gr { color: var(--muted); }

/* The same block in the hover tooltip. */
#tip .row2.grantrow .src b { font-family: var(--font-ui); color: var(--ink); }
#tip .row2.grantrow .src i {
	font-style: normal;
	color: var(--danger-text);
	font-size: 10px;
	border-bottom: 1px dotted var(--danger-text);
}

/* The buckets behind the number, shown only when more than one contributed. */
.workings {
	font-family: var(--font-data);
	font-size: 10.5px;
	color: var(--muted);
	margin-top: 2px;
}

.facets {
	display: flex;
	flex-wrap: wrap;
	gap: 4px 6px;
	margin-top: 3px;
}
.facet {
	font-family: var(--font-data);
	font-size: 10.5px;
	line-height: 1.5;
	padding: 1px 7px;
	border-radius: 999px;
	border: 1px solid transparent;
	/* Without this the pill wraps its own text and renders as a circle. */
	white-space: nowrap;
}
.facet.gear  { color: #d3a06a; border-color: rgba(211,160,106,0.35); }
.facet.attr  { color: #8fbf7a; border-color: rgba(143,191,122,0.35); }
.facet.skill { color: #6ba3d6; border-color: rgba(107,163,214,0.35); }
.facet.tree  { color: #b98ad0; border-color: rgba(185,138,208,0.35); }
.facet.relic { color: #d67a6b; border-color: rgba(214,122,107,0.35); }
.facet.charm { color: #6fc2b4; border-color: rgba(111,194,180,0.35); }
.facet.socket{ color: #9aa7c4; border-color: rgba(154,167,196,0.35); }
.facet.ether { color: #7fd1a0; border-color: rgba(127,209,160,0.35); }

/* ---------- recommendations ---------- */

/* A stat row is a control: pressing it ranks the tree routes that raise it.

   This is the product's headline feature and its entire entry point was the word
   "how?" at 10px and 45% opacity, measuring 31 by 14 pixels. Three things were
   wrong with that and all three are fixed here: it did not look like a control,
   it did not say what it did, and it was not legible. It is now a pill with a
   border, at full opacity, carrying a verb. The row is only drawn as a button at
   all where the recommender has something to say, so the pill is always an offer
   that can be honoured. */
.stat button.head:hover .ask { background: rgba(107, 163, 214, 0.16); }
.stat .ask {
	display: inline-block;
	margin-left: 8px;
	padding: 1px 8px;
	font-size: 10.5px;
	line-height: 1.5;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	white-space: nowrap;
	color: var(--path);
	border: 1px solid rgba(107, 163, 214, 0.55);
	border-radius: 999px;
	transition: background 120ms ease;
}
.stat.open .ask { background: rgba(107, 163, 214, 0.16); }
.stat.open { background: rgba(107, 163, 214, 0.05); border-radius: 8px; padding: 8px; margin: 0 -8px; }

.advice { display: flex; flex-direction: column; gap: 6px; margin-top: 9px; }

.offer {
	display: flex;
	align-items: center;
	gap: 10px;
	width: 100%;
	padding: 7px 9px;
	background: var(--ground);
	border: 1px solid var(--line);
	border-radius: 7px;
	text-align: left;
	cursor: pointer;
	min-height: 46px;
}
.offer:hover { border-color: var(--path); }
.offer:focus-visible { outline: 2px solid var(--path); outline-offset: 2px; }

.offer .og {
	flex: none;
	min-width: 58px;
	font-family: var(--font-data);
	font-variant-numeric: tabular-nums;
	font-size: 14px;
	color: var(--accent);
}
.offer .ob { flex: 1; min-width: 0; }
.offer .on {
	display: block;
	font-size: 12.5px;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.offer .oc {
	display: block;
	font-family: var(--font-data);
	font-size: 10.5px;
	color: var(--muted);
}
.offer .ot {
	flex: none;
	font-size: 10.5px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--path);
}

/* ---------- attributes ---------- */

.attrs { display: grid; gap: 8px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.attr {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 7px 9px;
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: 8px;
}
.attr .nm { flex: 1; min-width: 0; font-size: 13px; text-transform: capitalize; }
/* The number is the control. Capping one attribute was 401 presses of a 32px
   button and a whole character was 2,400, so this is editable in place and the
   steppers became the fine adjustment rather than the only adjustment. It is
   dressed as a field, because a number that can be typed into and does not look
   like it can is the same as one that cannot. */
.attr .v {
	font-family: var(--font-data);
	font-variant-numeric: tabular-nums;
	font-size: 14px;
	color: var(--accent);
	min-width: 46px;
	min-height: 32px;
	padding: 4px 6px;
	background: var(--ground);
	border: 1px solid var(--line);
	border-radius: 6px;
	text-align: right;
	cursor: text;
	/* A contenteditable wraps and grows; this one holds one short number. */
	white-space: nowrap;
	overflow: hidden;
}
.attr .v:hover { border-color: var(--accent-dim); }
.attr .v:focus { border-color: var(--accent-dim); outline: none; }
.attr .v:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.attr button {
	width: 32px; height: 32px;
	flex: none;
	border-radius: 6px;
	background: var(--surface-2);
	border: 1px solid var(--line);
	font-size: 16px;
	line-height: 1;
	cursor: pointer;
}
.attr button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The Shift step existed and nothing said so, which is the same as not having
   it. On its own line so it can wrap without pushing the cap note around. */
.label .sub2.tip { display: block; font-size: 11.5px; }
@media (min-width: 720px) {
	.attrs { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Past this the tabs move under the header and the stats become a permanent
   rail, so the board and the numbers are readable at once. */
@media (min-width: 1000px) {
	#app {
		grid-template-rows: var(--bar) var(--tabs) 1fr;
		grid-template-columns: minmax(0, 1fr) 340px;
	}
	#bar   { grid-row: 1; grid-column: 1 / -1; }
	/* The stats tab is redundant once the rail is permanent. */
	#tabs  {
		grid-row: 2; grid-column: 1;
		border-top: none; border-bottom: 1px solid var(--line);
	}
	#panes { grid-row: 3; grid-column: 1; }
	/* With room, the summary is a rail: always open, no strip, no toggle. */
	#summary {
		grid-row: 2 / span 3;
		grid-column: 2;
		border-left: 1px solid var(--line);
		border-top: none;
		overflow-y: auto;
	}
	#summaryToggle { display: none; }
	/* A phone anchor on a desktop is wasted space and looks like a mistake. */
	#adAnchor { display: none; }
	/* Disabled: the rail holds the build summary, and that panel is the thing a
	   player reads constantly. Reserved markup is still present, so turning it
	   back on is one selector. */
	:root[data-ads="on"] #adRail {
		display: grid;
		flex: none;
		width: 300px; height: 250px;
		margin: 18px auto 24px;
	}
	#summaryBody { max-height: none !important; padding: 18px 18px 30px; }
	.slots { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/*
 * The left pillar.
 *
 * Gated at 1280 rather than 1000 on purpose. The rail already takes 340px, so
 * turning the pillar on any earlier would leave the planner itself under 500px
 * wide, which is narrower than the phone layout it replaced. An ad that makes
 * the tool worse than not having it is not worth the impression.
 */
@media (min-width: 1280px) and (min-height: 780px) {
	/* The track is the creative plus its gutters. Sizing it at 160 and then
	   giving the element a margin made the element wider than its own column,
	   so it spilled 12px into the planner. An ad may never cover content. */
	#app { grid-template-columns: 184px minmax(0, 1fr) 340px; }
	#bar      { grid-column: 1 / -1; }
	#adPillar {
		display: grid;
		grid-row: 2 / span 2;
		grid-column: 1;
		align-self: start;
		justify-self: center;
		width: 160px; height: 600px;
		margin: 14px 0 0;
	}
	#tabs     { grid-column: 2; }
	#panes    { grid-column: 2; }
	#summary  { grid-column: 3; }
}

/* Stated once, for both properties. The rule used to appear twice and name
   only `transition`, so any animation added later would have run regardless of
   what the visitor had asked their system for. */
@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		transition: none !important;
		animation: none !important;
	}
}


/* ---------- farming atlas ---------- */

#dropbar {
	display: flex;
	gap: 6px;
	margin-bottom: 14px;
	flex-wrap: wrap;
}
.seg {
	background: var(--surface-2);
	border: 1px solid var(--line);
	border-radius: 6px;
	padding: 8px 12px;
	font-size: 13px;
	min-height: 38px;
	cursor: pointer;
	white-space: nowrap;
}
.seg.on { border-color: var(--accent); color: var(--accent); }
#dropSearch {
	flex: 1;
	min-width: 140px;
	background: var(--ground);
	border: 1px solid var(--line);
	border-radius: 6px;
	padding: 8px 10px;
	font-size: 14px;
	min-height: 38px;
}
#dropSearch:focus { border-color: var(--accent-dim); }
#dropSearch:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }

.droplist { display: grid; gap: 6px; margin-bottom: 18px; }

/* A row and the detail it opens are siblings, not nested, so a long detail
   never makes its own row unclickable. */
.drow {
	display: flex;
	align-items: center;
	gap: 10px;
	width: 100%;
	padding: 7px 10px;
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: 8px;
	text-align: left;
	cursor: pointer;
	min-height: 46px;
}
.drow:hover { border-color: var(--accent-dim); }
.drow.on { border-color: var(--accent); }
.drow .ico {
	width: 28px; height: 28px; flex: none;
	border-radius: 5px;
	background-color: var(--ground);
	image-rendering: pixelated;
}
.drow .ico.pin { background-color: var(--ground); border: 1px solid var(--line); }
/* A small marker inline with text, for rows that name a place in passing. */
.kmark {
	display: inline-grid;
	place-items: center;
	width: 14px; height: 14px;
	vertical-align: -3px;
	margin-right: 5px;
}
.drow .db { min-width: 0; flex: 1; display: flex; flex-direction: column; }
.drow .nm { font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.drow .meta { font-size: 11px; color: var(--muted); text-transform: capitalize; }

.ddetail {
	margin: -2px 0 4px;
	padding: 8px 12px;
	background: var(--ground);
	border: 1px solid var(--line);
	border-top: none;
	border-radius: 0 0 8px 8px;
	max-height: 420px;
	overflow-y: auto;
}
.ddetail .ico.sm {
	display: inline-grid;
	width: 20px; height: 20px;
	vertical-align: -5px;
	margin-right: 6px;
}

@container pane (min-width: 760px) {
	/* Two columns of rows once there is width; the detail spans both so it is
	   never squeezed into half the pane. */
	.droplist { grid-template-columns: repeat(2, minmax(0, 1fr)); }
	.ddetail { grid-column: 1 / -1; border-top: 1px solid var(--line); border-radius: 8px; }
}

/* A raw weight, not an odds figure. Muted so it never reads as a rate. */
.row2 b.wt { color: var(--muted); font-weight: 400; cursor: help; }


/* ---------- item editor ---------- */

/* The lower area used to repeat every equipped affix, which every tooltip
   already says. It is worth more as the place you describe an item you own. */
.editor {
	margin-top: 24px;
	padding: 14px 16px 18px;
	background: var(--surface);
	border: 1px solid var(--accent-dim);
	border-radius: 10px;
}
.edhead { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.edhead h3 { margin: 0; font-family: var(--font-display); font-size: 18px; font-weight: 400; }
.edrow {
	display: flex;
	align-items: center;
	gap: 10px;
	flex-wrap: wrap;
	margin: 12px 0;
}
.edrow .label { margin: 0; min-width: 74px; }
.edtiers { display: flex; gap: 4px; }
.edtiers .seg { padding: 6px 10px; min-width: 34px; font-family: var(--font-data); }
/* A tier the client never defines this base at. Shown, so the band is legible,
   but plainly not one of its own. */
/* 0.4 measured 3.43:1. The dashed border already says "not at this tier". */
.edtiers .seg.ghost { opacity: 0.6; border-style: dashed; }
.edrow select {
	background: var(--ground);
	border: 1px solid var(--line);
	border-radius: 6px;
	padding: 7px 9px;
	min-height: 36px;
}

.edaffix {
	margin: 8px 0;
	padding: 8px 10px;
	background: var(--ground);
	border: 1px solid var(--line);
	border-radius: 8px;
}
/* The roll: what it is, where in its range you put it, and what the range was.
   Showing the range matters, because "47" means nothing without "12 to 50". */
.edroll {
	display: grid;
	grid-template-columns: minmax(0, 1fr) 130px 44px auto;
	align-items: center;
	gap: 8px;
	padding: 3px 0;
	font-size: 13px;
}
.edroll b {
	font-family: var(--font-data);
	font-variant-numeric: tabular-nums;
	text-align: right;
	color: var(--accent);
}
.edroll input[type="range"] { width: 100%; accent-color: var(--accent); }

.tierpill {
	margin-left: 7px;
	padding: 1px 6px;
	border-radius: 4px;
	border: 1px solid var(--accent-dim);
	color: var(--accent);
	font-family: var(--font-data);
	font-size: 10px;
	letter-spacing: 0.06em;
}

/* Crafting is offered where the choosing happens: the first row of the picker.
   It was a pencil on every slot, which looked like clutter and was not
   findable. */
.pick.craftrow { border: 1px dashed var(--accent-dim); }
.pick.craftrow .nm { color: var(--accent); }

@media (max-width: 560px) {
	.edroll { grid-template-columns: minmax(0, 1fr) 44px; }
	.edroll input[type="range"] { grid-column: 1 / -1; }
	.edroll .src:last-child { display: none; }
}


/* The craft row's anvil, drawn inline rather than pulled from an atlas. */
.ico.anvilbox { border: 1px dashed var(--accent-dim); background: none; }
.anvil { width: 20px; height: 20px; color: var(--accent); }
/* The tier and window an affix actually rolled at, on a crafted item. */
#tip .row2 .rolled {
	margin-left: 6px;
	color: var(--accent);
	font-style: normal;
	font-family: var(--font-data);
	font-size: 10px;
}


/* ---------- runewords ---------- */

/* The socket count is the real gate on a runeword, so it sits where every other
   list puts an icon. */
.drow .ico .rwn {
	font-family: var(--font-data);
	font-size: 13px;
	color: var(--accent);
}
/* The recipe, in order, because order is the recipe. */
.runeline {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 6px;
	padding: 4px 0 10px;
}
.runechip {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 4px 9px 4px 5px;
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: 6px;
	font-size: 12px;
}
.runechip .ico { width: 20px; height: 20px; }
.runearrow { color: var(--muted); }
