/*
 * Nivoli Games front end.
 *
 * Hand-written, no build step, no framework, no external font and no request to
 * anything but this file. Loaded only on the plugin's own templates (see
 * Template\Assets), so the rest of the site is untouched.
 *
 * Everything below is scoped under `.ng`, the wrapper each template opens with.
 * The one exception is the fixed reading-progress bar, which cannot live inside
 * a scrolled container, and the two `html.ng-js` hooks the script sets.
 *
 * The custom properties are the whole design system. Retune the six at the top
 * and the rest follows: every colour, rule, panel and shadow is derived from
 * them with color-mix, and each derivation ships a flat hex fallback first for
 * browsers without it.
 */

:root {
	/* ---- the six ---------------------------------------------------------- */
	--ng-bg: #262320;        /* page background, inherited from the site       */
	--ng-fg: #ddd6c6;        /* body text: 10.8:1 on the background           */
	--ng-accent: #e8b45e;    /* links, focus rings, counts: 8.28:1            */
	--ng-muted: #a89f8e;     /* secondary text: 5.96:1, AA at any size        */
	--ng-rule: #423d36;      /* decorative hairlines: row dividers, underlines */
	--ng-control-border: var(--ng-muted); /* control edges: az links, selects, page numbers, expander summaries. --ng-rule is only 1.45:1 against the background, a divider you are not meant to notice, not the 3:1 floor a control's own edge needs; --ng-muted is the same colour already verified at 5.96:1 for text, comfortably clear of that floor as a border */
	--ng-measure: 70ch;      /* prose column width                            */

	/* ---- derived ---------------------------------------------------------- */
	--ng-panel: #2f2b27;
	--ng-panel: color-mix(in srgb, var(--ng-fg) 7%, var(--ng-bg));
	--ng-panel-2: #35302b;
	--ng-panel-2: color-mix(in srgb, var(--ng-fg) 12%, var(--ng-bg));
	--ng-code-bg: #1e1b19;
	--ng-code-bg: color-mix(in srgb, #000 22%, var(--ng-bg));
	--ng-accent-ink: #1a1816;
	--ng-accent-ink: color-mix(in srgb, #000 30%, var(--ng-bg));
	--ng-accent-soft: #3a3227;
	--ng-accent-soft: color-mix(in srgb, var(--ng-accent) 14%, var(--ng-bg));

	/* ---- type and rhythm -------------------------------------------------- */
	--ng-font: Inter, "Inter var", "Helvetica Neue", Helvetica, Arial, system-ui, sans-serif;
	--ng-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "DejaVu Sans Mono", monospace;
	/* The plugin's own root. `rem` is relative to the document root, and a
	   theme is free to set that to anything: newsgamer ships the old 62.5%
	   trick, `html { font-size: 10px }`, which rendered every size below at
	   62% of its intended value. Sizes are derived from this token instead,
	   so no theme can rescale the plugin's templates again. */
	--ng-root: 16px;
	--ng-size: calc(var(--ng-root) * 1.1875);   /* 19px */
	--ng-leading: 1.7;
	--ng-gap: calc(var(--ng-root) * 1.25);
	--ng-radius: 4px;
	/* The widest a game cover is ever rendered, and so the width of the card
	   that frames one. It matches the width of the guide page's own side
	   column, so the cap never binds there and only takes effect where that
	   column is not a column: below 960px the aside stacks above the prose at
	   the full width of the shell, and without this a cover was rendered at
	   whatever that came to. On a 900px window a 795x1060 cover was being
	   blown up to roughly 870x1160, a page and a half of upscaled artwork
	   before the first line of the guide, which is the "huge zoomed crop" this
	   token exists to prevent. */
	--ng-cover-max: calc(var(--ng-root) * 15);
}

/* ---------------------------------------------------------------- shell ---- */

.ng {
	box-sizing: border-box;
	max-width: calc(var(--ng-root) * 74);
	margin: 0 auto;
	padding: calc(var(--ng-root) * 1.5) clamp(calc(var(--ng-root) * 1), 4vw, calc(var(--ng-root) * 2)) calc(var(--ng-root) * 4);
	color: var(--ng-fg);
	font-family: var(--ng-font);
	font-size: var(--ng-size);
	line-height: var(--ng-leading);
	/* Nothing inside a plugin template may widen the page. Long unbroken
	   strings (1990s URLs, file paths) wrap instead of pushing the layout. */
	overflow-wrap: break-word;
}

.ng *,
.ng *::before,
.ng *::after {
	box-sizing: inherit;
}

.ng img {
	max-width: 100%;
	height: auto;
}

.ng a {
	color: var(--ng-accent);
	text-decoration-thickness: 1px;
	text-underline-offset: 0.15em;
}

.ng a:hover {
	text-decoration-thickness: 2px;
}

/* One focus treatment for everything interactive, including the scrollable
   `pre` containers, which are focusable so a keyboard can scroll them. */
.ng a:focus,
.ng button:focus,
.ng select:focus,
.ng summary:focus,
.ng [tabindex]:focus {
	outline: 2px solid var(--ng-accent);
	outline-offset: 2px;
	border-radius: 2px;
}

.ng a:focus:not(:focus-visible),
.ng button:focus:not(:focus-visible),
.ng select:focus:not(:focus-visible),
.ng summary:focus:not(:focus-visible) {
	outline: none;
}

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

/* ----------------------------------------------------------- typography ---- */

.ng-title {
	margin: 0 0 calc(var(--ng-root) * 0.5);
	font-size: clamp(calc(var(--ng-root) * 1.75), 4vw, calc(var(--ng-root) * 2.5));
	line-height: 1.15;
	letter-spacing: -0.01em;
}

.ng-eyebrow {
	margin: 0 0 calc(var(--ng-root) * 0.25);
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.8);
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

.ng-section {
	margin: calc(var(--ng-root) * 2.5) 0 0;
	/* A wide table or map inside a section must not stretch the section. */
	min-width: 0;
}

.ng-section-heading {
	display: flex;
	align-items: baseline;
	gap: calc(var(--ng-root) * 0.5);
	margin: 0 0 calc(var(--ng-root) * 0.75);
	padding-bottom: calc(var(--ng-root) * 0.4);
	border-bottom: 1px solid var(--ng-rule);
	font-size: calc(var(--ng-root) * 1.25);
	line-height: 1.3;
}

.ng-empty,
.ng-notice {
	margin: calc(var(--ng-root) * 0.5) 0;
	color: var(--ng-muted);
}

/*
 * A facet archive offers two ways out (the company behind the term, and the
 * whole catalogue), so this row can hold more than one link: laid out as a
 * wrapping flex row with a gap wide enough that two tap targets are never one,
 * and each link tall enough to be one.
 */
.ng-backlink {
	display: flex;
	flex-wrap: wrap;
	gap: calc(var(--ng-root) * 0.5) calc(var(--ng-root) * 1.5);
	margin: calc(var(--ng-root) * 3) 0 0;
	padding-top: calc(var(--ng-root) * 1);
	border-top: 1px solid var(--ng-rule);
	font-size: calc(var(--ng-root) * 0.95);
}

.ng-backlink a {
	display: inline-flex;
	align-items: center;
	min-height: calc(var(--ng-root) * 2.75);
}

/* ---------------------------------------------------------------- prose ---- */

.ng-prose {
	max-width: var(--ng-measure);
	min-width: 0;
}

.ng-prose > * + * {
	margin-top: 1.15em;
}

.ng-prose p,
.ng-prose li {
	margin-top: 0;
	margin-bottom: 0;
}

.ng-prose h2,
.ng-prose h3,
.ng-prose h4 {
	margin: 2.2em 0 0.6em;
	line-height: 1.25;
	/* Anchor targets sit below the sticky game bar rather than under it. */
	scroll-margin-top: calc(var(--ng-root) * 5);
}

.ng-prose h2 {
	font-size: 1.4em;
}

.ng-prose h3 {
	font-size: 1.15em;
}

.ng-prose h4 {
	font-size: 1em;
	color: var(--ng-muted);
	text-transform: uppercase;
	letter-spacing: 0.06em;
}

.ng-prose ul,
.ng-prose ol {
	padding-left: 1.4em;
}

.ng-prose li + li {
	margin-top: 0.4em;
}

.ng-prose blockquote {
	margin-left: 0;
	margin-right: 0;
	padding: 0.2em 0 0.2em 1em;
	border-left: 3px solid var(--ng-rule);
	color: var(--ng-muted);
}

.ng-prose hr {
	border: 0;
	border-top: 1px solid var(--ng-rule);
}

.ng-prose img {
	border-radius: var(--ng-radius);
}

/*
 * Neutralise legacy inline typography carried over by the same Joomla/EE
 * migration the theme's `.ja-prose` rule fixes for ordinary posts (see
 * wp-nivoli-ja/assets/css/theme.css): measured across published posts and
 * walkthroughs, 2,414 carry a dark inline `color` and 1,892 carry an inline
 * `pt` font size, and `post_content` is never rewritten, so this has to run
 * at render time. Same reasoning as the theme's rule: an inline style
 * outranks any selector, so only `!important` wins here; scoped to text
 * level elements only, never `img` (its inline `style` carries real
 * `max-width` sizing); `font-weight`/`font-style` are left out so semantic
 * bold and italic keep rendering.
 */
.ng-prose span, .ng-prose p, .ng-prose div, .ng-prose td, .ng-prose th,
.ng-prose li, .ng-prose b, .ng-prose i, .ng-prose strong, .ng-prose em {
	color: inherit !important;
	background: inherit !important;
	font-family: inherit !important;
	font-size: inherit !important;
}

.ng-prose code {
	padding: 0.1em 0.35em;
	background: var(--ng-code-bg);
	border-radius: 3px;
	font-family: var(--ng-mono);
	font-size: max(12px, 0.9em);
}

/* The 838 guides that converted to paragraphs with no headings at all. Slightly
   tighter than the outlined ones: with nothing to break up the page, generous
   paragraph spacing reads as a list of unrelated sentences. */
.ng-prose--flat > * + * {
	margin-top: 1em;
}

/* --------------------------------------- preserved monospace and scrollers -- */

/*
 * The 123 guides kept byte for byte. This is the intended rendering, not a
 * fallback: they contain ASCII maps and space-aligned tables, so the text is
 * monospace, it does NOT wrap, and the block scrolls sideways on its own instead
 * of making the page scroll. Comfortable line height and a low-contrast panel
 * make a wall of fixed-width text readable rather than merely preserved.
 */
/*
 * Scrollbars on the plugin's own scroll containers: the preserved-block
 * scrollers here, the sticky side rail and the small-screen contents sheet.
 * The default scrollbar is a wide light control drawn for a white page; on
 * this palette it reads as a bright bar cut through the layout. The thumb
 * sits between --ng-rule (a divider you are not meant to notice) and
 * --ng-muted (text): visible as a control, quiet as furniture, brightening
 * to --ng-muted under the pointer. `scrollbar-width/color` is the standard
 * pair (Firefox, Chromium 121+, Safari 18.2+); the `::-webkit-scrollbar`
 * rules cover older WebKit, and engines that honour the standard properties
 * ignore them.
 */
.ng-scroller,
.ng-side,
.ng-toc {
	scrollbar-width: thin;
	scrollbar-color: var(--ng-rule) transparent;
}

.ng-scroller::-webkit-scrollbar,
.ng-side::-webkit-scrollbar,
.ng-toc::-webkit-scrollbar {
	width: 8px;
	height: 8px;
}

.ng-scroller::-webkit-scrollbar-track,
.ng-side::-webkit-scrollbar-track,
.ng-toc::-webkit-scrollbar-track {
	background: transparent;
}

.ng-scroller::-webkit-scrollbar-thumb,
.ng-side::-webkit-scrollbar-thumb,
.ng-toc::-webkit-scrollbar-thumb {
	background: #4a453e;
	background: color-mix(in srgb, var(--ng-fg) 22%, var(--ng-bg));
	border-radius: 4px;
}

.ng-scroller::-webkit-scrollbar-thumb:hover,
.ng-side::-webkit-scrollbar-thumb:hover,
.ng-toc::-webkit-scrollbar-thumb:hover {
	background: var(--ng-muted);
}

.ng-scroller {
	max-width: 100%;
	margin: calc(var(--ng-root) * 1.5) 0;
	overflow-x: auto;
	overflow-y: hidden;
	background: var(--ng-code-bg);
	border: 1px solid var(--ng-rule);
	border-left: 3px solid var(--ng-accent-soft);
	border-radius: var(--ng-radius);
	/* Scroll one cell at a time on touch, and tell the browser this element
	   owns horizontal gestures so a swipe does not navigate back. */
	overscroll-behavior-x: contain;
}

.ng-scroller pre,
.ng-prose pre {
	margin: 0;
	padding: calc(var(--ng-root) * 1) calc(var(--ng-root) * 1.15);
	background: none;
	border: 0;
	color: var(--ng-fg);
	font-family: var(--ng-mono);
	font-size: calc(var(--ng-root) * 0.86);
	line-height: 1.55;
	/* Never `pre-wrap`: wrapping is what destroys the maps. */
	white-space: pre;
	tab-size: 8;
	-moz-tab-size: 8;
}

.ng-scroller--table {
	padding: 0;
}

.ng-scroller table {
	margin: 0;
	border-collapse: collapse;
	font-size: calc(var(--ng-root) * 0.95);
}

.ng-scroller th,
.ng-scroller td {
	padding: calc(var(--ng-root) * 0.4) calc(var(--ng-root) * 0.7);
	border: 1px solid var(--ng-rule);
	text-align: left;
	vertical-align: top;
}

.ng-notice--preserved {
	max-width: var(--ng-measure);
	margin: 0 0 calc(var(--ng-root) * 1.5);
	padding: calc(var(--ng-root) * 0.75) calc(var(--ng-root) * 1);
	background: var(--ng-panel);
	border-left: 3px solid var(--ng-accent-soft);
	border-radius: var(--ng-radius);
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.9);
	line-height: 1.55;
}

/* -------------------------------------------------------------- spoilers ---- */

.ng-spoiler {
	max-width: var(--ng-measure);
	margin: calc(var(--ng-root) * 1.5) 0;
	padding: calc(var(--ng-root) * 0.25) calc(var(--ng-root) * 1);
	background: var(--ng-panel);
	border: 1px dashed var(--ng-rule);
	border-radius: var(--ng-radius);
}

.ng-spoiler-summary {
	padding: calc(var(--ng-root) * 0.6) 0;
	color: var(--ng-accent);
	cursor: pointer;
	font-size: calc(var(--ng-root) * 0.95);
	font-weight: 600;
}

.ng-spoiler[open] .ng-spoiler-summary {
	margin-bottom: calc(var(--ng-root) * 0.5);
	border-bottom: 1px solid var(--ng-rule);
}

.ng-spoiler-body > *:first-child {
	margin-top: 0;
}

/* ---------------------------------------------------------- breadcrumbs ---- */

.ng-crumbs {
	margin: 0 0 calc(var(--ng-root) * 1.5);
	/* .ng-crumb-current below is muted-coloured, and --ng-muted is only used
	   at 14px and larger; 0.875 * the 16px root is exactly that floor. */
	font-size: calc(var(--ng-root) * 0.875);
}

.ng-crumbs-list {
	display: flex;
	flex-wrap: wrap;
	gap: calc(var(--ng-root) * 0.35) calc(var(--ng-root) * 0.6);
	margin: 0;
	padding: 0;
	list-style: none;
	color: var(--ng-muted);
}

.ng-crumb + .ng-crumb::before {
	content: "/";
	margin-right: calc(var(--ng-root) * 0.6);
	color: var(--ng-rule);
}

.ng-crumb-current {
	color: var(--ng-muted);
}

/* -------------------------------------------- buttons, chips and badges ---- */

/*
 * Two selectors on every button rule, on purpose. A button is usually an `<a>`,
 * and `.ng a` (one class, one element) outranks a bare `.ng-button` (one class):
 * with a single selector the label inherits the link colour and renders amber on
 * amber. `.ng .ng-button` outranks it and covers `<button>` too.
 */
.ng-button,
.ng .ng-button {
	display: inline-block;
	padding: calc(var(--ng-root) * 0.5) calc(var(--ng-root) * 0.95);
	background: var(--ng-accent);
	border: 1px solid var(--ng-accent);
	border-radius: var(--ng-radius);
	color: var(--ng-accent-ink);
	font: inherit;
	font-size: calc(var(--ng-root) * 0.95);
	font-weight: 600;
	text-decoration: none;
	cursor: pointer;
}

.ng-button:hover,
.ng .ng-button:hover {
	background: color-mix(in srgb, var(--ng-accent) 85%, #fff);
	color: var(--ng-accent-ink);
}

.ng-button--quiet,
.ng .ng-button--quiet {
	background: none;
	border-color: var(--ng-control-border);
	color: var(--ng-fg);
	font-weight: 400;
}

.ng-button--quiet:hover,
.ng .ng-button--quiet:hover {
	background: var(--ng-panel);
	color: var(--ng-fg);
}

.ng-chips {
	display: flex;
	flex-wrap: wrap;
	column-gap: calc(var(--ng-root) * 0.4);
	/*
	 * Taller than the column gap on purpose: .ng-chip a below extends its own
	 * hit area about 15px above and below the pill it wraps, invisibly. Rows
	 * closer together than twice that would let one chip's hit area reach
	 * into the row next to it.
	 */
	row-gap: calc(var(--ng-root) * 1.75);
	margin: calc(var(--ng-root) * 0.75) 0 0;
	padding: 0;
	list-style: none;
}

.ng-chip {
	padding: calc(var(--ng-root) * 0.2) calc(var(--ng-root) * 0.6);
	background: var(--ng-panel);
	border: 1px solid var(--ng-rule);
	border-radius: 999px;
	font-size: calc(var(--ng-root) * 0.85);
}

/*
 * The audit measured this pill at 17px tall: a platform tag reads better
 * small, so rather than pad the pill itself up to 44px and inflate the type
 * with it, the link's own hit area is grown around the same visible text.
 * `min-height` plus flex centring sets the box that actually gets hit
 * tested to the 44px floor; the matching negative margin (half of the
 * height this adds beyond one line of text) folds the extra space back out
 * of the flow so the visible pill, and the row height .ng-chips reserves
 * for it, are unchanged.
 */
.ng-chip a {
	display: flex;
	align-items: center;
	justify-content: center;
	line-height: calc(var(--ng-root) * 0.85);
	min-height: calc(var(--ng-root) * 2.75);
	margin: calc(var(--ng-root) * -0.95) 0;
	text-decoration: none;
}

.ng-chip a:hover {
	text-decoration: underline;
}

.ng-chip--active {
	display: inline-flex;
	align-items: center;
	gap: calc(var(--ng-root) * 0.4);
	background: var(--ng-accent-soft);
}

.ng-chip-remove {
	text-decoration: none;
	font-size: 1.1em;
	line-height: 1;
}

/* Full body colour rather than muted: at 0.75em on the second panel tone the
   muted value lands at 4.38:1, which is under AA, and a count is information. */
.ng-count {
	padding: calc(var(--ng-root) * 0.05) calc(var(--ng-root) * 0.45);
	background: var(--ng-panel-2);
	border-radius: 999px;
	color: var(--ng-fg);
	font-size: max(12px, 0.75em);
	font-weight: 600;
	font-variant-numeric: tabular-nums;
}

.ng-tag {
	display: inline-block;
	margin-left: calc(var(--ng-root) * 0.35);
	padding: 0 calc(var(--ng-root) * 0.4);
	border: 1px solid var(--ng-rule);
	border-radius: 3px;
	color: var(--ng-muted);
	font-family: var(--ng-mono);
	/* --ng-muted is only used at 14px and larger; 0.875 * the 16px root is
	   exactly that floor. Every "pre" guide in the library index carries one
	   of these, so this is not a rare corner. */
	font-size: calc(var(--ng-root) * 0.875);
	text-transform: lowercase;
	white-space: nowrap;
}

/* Search results. These two are deliberately not scoped to `.ng`: they render
   inside the theme's own search template, where there is no `.ng` wrapper. */
.ng-badge {
	display: inline-block;
	margin-right: calc(var(--ng-root) * 0.4);
	padding: calc(var(--ng-root) * 0.05) calc(var(--ng-root) * 0.45);
	background: var(--ng-accent-soft);
	border: 1px solid var(--ng-rule);
	border-radius: 3px;
	color: var(--ng-accent);
	font-size: max(12px, 0.75em);
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	vertical-align: 0.08em;
}

.ng-result-meta {
	color: var(--ng-muted);
	font-size: max(12px, 0.85em);
}

/* ------------------------------------------------------- game page hero ---- */

.ng-hero {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: calc(var(--ng-root) * 1.5);
	margin: 0 0 calc(var(--ng-root) * 2);
}

.ng-hero-art {
	max-width: calc(var(--ng-root) * 16);
}

.ng-cover-img {
	display: block;
	width: 100%;
	background: var(--ng-panel);
	border: 1px solid var(--ng-rule);
	border-radius: var(--ng-radius);
}

.ng-cover-noart {
	display: grid;
	place-items: center;
	aspect-ratio: 3 / 4;
	background: var(--ng-panel);
	border: 1px solid var(--ng-rule);
	border-radius: var(--ng-radius);
	color: var(--ng-rule);
	font-size: calc(var(--ng-root) * 4);
	font-weight: 700;
}

/*
 * The release year, behind the game's name in the same heading. Lighter and
 * muted so the name carries the weight: at the heading's 28px to 40px the
 * muted value is far clear of any contrast floor, which is not true of the
 * 12px and 13px labels elsewhere on this page.
 */
.ng-title-year {
	color: var(--ng-muted);
	font-weight: 400;
	white-space: nowrap;
}

/*
 * The header's facts, one labelled row each.
 *
 * 0.4.6 labelled the rows, which the owner kept, and gave all six of them the
 * same pills, which is what this replaces: sixteen outlined chips down six rows,
 * so the only thing saying which kind of fact you were reading was the 14px
 * label beside it, and a "+6 more" expander that answered a long credit row by
 * dumping a second row of the same pills under it and shoving the layout.
 *
 * Fewer pills, not more kinds of pill. Only the platform row keeps chips,
 * because eleven platforms are a set you scan and each one is somewhere to go.
 * A credit is prose (.ng-names). A series is a destination (.ng-facts-place).
 * Setting and perspective share one muted row (.ng-quiet) and carry the least
 * weight of anything in the header, being the least-scanned facts on it.
 *
 * --ng-facts-line is the whole vertical rhythm: the label and every kind of
 * value share one line-height, so the rows line up as a table rather than each
 * sitting at whatever height its own type happens to want. Row heights are
 * uniform to within the 1.8px the chip row's border adds.
 *
 * A two-column grid, label then values, collapsing to stacked rows on a narrow
 * screen where a 112px label column would leave a chip per line.
 */
.ng-facts {
	--ng-facts-line: calc(var(--ng-root) * 1.65);
	/* The 44px floor, and the half of it a control has to borrow from the rows
	   around it: (44 - 26.4) / 2 = 8.8px, which is 0.55 of the root. Every
	   control in this block grows its hit box with the same pair of numbers. */
	--ng-facts-hit: calc(var(--ng-root) * 2.75);
	--ng-facts-lift: calc(var(--ng-root) * -0.55);
	display: grid;
	gap: calc(var(--ng-root) * 0.5);
	margin: calc(var(--ng-root) * 0.75) 0 0;
}

.ng-facts-row {
	display: grid;
	grid-template-columns: calc(var(--ng-root) * 7) minmax(0, 1fr);
	column-gap: calc(var(--ng-root) * 0.75);
	align-items: start;
}

/*
 * The eyebrow treatment, one step up in size: uppercase, letter-spaced, muted.
 * 14px rather than the 12.8px .ng-eyebrow uses, because --ng-muted is only
 * used at 14px and larger and 0.875 * the 16px root is exactly that floor.
 * The line-height is the block's shared one, so the label sits on the centre
 * line of whatever is beside it without either being nudged.
 */
.ng-facts-label {
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.875);
	letter-spacing: 0.08em;
	line-height: var(--ng-facts-line);
	text-transform: uppercase;
}

.ng-facts-values {
	margin: 0;
	min-width: 0;
	line-height: var(--ng-facts-line);
}

/*
 * The platform row, and the only row with chips on it.
 *
 * Tighter than .ng-chips is elsewhere, in both directions. The row-gap was 28px
 * against a 22px pill, which is what the owner was looking at when they called
 * the wrap awkward: a gap taller than the thing it separates reads as two lists
 * rather than one wrapped one. The pill is a little taller here and the gap a
 * lot shorter, so eleven platforms wrap into two lines that sit together.
 *
 * The gap cannot go below twice the hit area .ng-chip a grows past the pill's
 * own edge, or one line's invisible tap target reaches into the next. With
 * 6.4px of padding on the pill that overhang is 7.8px, so 16px is the floor
 * and 16px is what this sets.
 */
.ng-facts .ng-chips {
	column-gap: calc(var(--ng-root) * 0.35);
	row-gap: calc(var(--ng-root) * 1);
	margin: 0;
}

.ng-facts .ng-chip {
	padding: calc(var(--ng-root) * 0.4) calc(var(--ng-root) * 0.6);
}

/*
 * A credit row: linked company names as a sentence, at ordinary reading size in
 * the accent colour, underlined the way every other prose link in the plugin
 * is. Not pills. "Microïds Canada" is a fact about the game, and a pill around
 * it made it look like one of the eleven filters above.
 */
.ng-names {
	display: block;
	font-size: var(--ng-root);
	line-height: var(--ng-facts-line);
}

/*
 * The inline expander. It continues the sentence rather than opening anything:
 * pressed, the names it was holding appear in front of it, mid-line, and the
 * label becomes "fewer". Nothing is added beside the row and nothing under it.
 *
 * A control, so it draws its own edge with --ng-control-border rather than the
 * decorative --ng-rule the chips use, and --ng-fg rather than the muted colour,
 * for the reason .ng-count gives: a label under 14px does not get the muted
 * value. 14px here, which is the floor anyway.
 *
 * The 44px tap target is grown the way .ng-chip a grows one: min-height plus
 * centring sets the box that gets hit tested, and the matching negative margin
 * folds the extra height back out of the line, so the tallest thing on this row
 * is still one line of text. The chrome is on an inner span for the same reason
 * the 0.4.6 pill was: min-height sets the button's painted box as well as its
 * hit box, so a background on the button itself would draw a 44px lozenge in a
 * row of 26px lines. The left margin is the space in front of the word "and": a
 * literal space in the markup would be left behind mid-sentence in the one
 * rendering where this button is not there. See parts/fact-names.php.
 */
.ng-names-more {
	display: inline-flex;
	align-items: center;
	min-height: var(--ng-facts-hit);
	margin: var(--ng-facts-lift) 0 var(--ng-facts-lift) calc(var(--ng-root) * 0.35);
	padding: 0;
	background: none;
	border: 0;
	color: var(--ng-fg);
	font-family: inherit;
	font-size: calc(var(--ng-root) * 0.875);
	font-weight: 600;
	line-height: var(--ng-facts-line);
	white-space: nowrap;
	cursor: pointer;
}

.ng-names-more-pill {
	padding: calc(var(--ng-root) * 0.2) calc(var(--ng-root) * 0.6);
	background: var(--ng-panel-2);
	border: 1px solid var(--ng-control-border);
	border-radius: 999px;
}

.ng-names-more:hover .ng-names-more-pill {
	background: var(--ng-accent-soft);
}

/*
 * `hidden` has to win here. The attribute's `display: none` comes from the user
 * agent sheet, and the class rules above are the same specificity and later in
 * the cascade, so without this the button the server hides would render anyway
 * and a reader with no script would see an inert control.
 */
.ng .ng-names-more[hidden],
.ng .ng-names-rest[hidden],
.ng .ng-names-more-open[hidden],
.ng .ng-names-more-shut[hidden] {
	display: none;
}

/*
 * The series row: a destination, not a fact. Bigger than the label beside it,
 * weighted, with an arrow saying what following it does, and the same 44px hit
 * box the other controls take. The arrow leans into the direction it points on
 * hover; the reduced-motion block at the end of this file already flattens that
 * along with every other transition inside .ng.
 */
.ng-facts-place {
	display: inline-flex;
	align-items: center;
	gap: calc(var(--ng-root) * 0.35);
	min-height: var(--ng-facts-hit);
	margin: var(--ng-facts-lift) 0;
	font-size: calc(var(--ng-root) * 1.0625);
	font-weight: 600;
	line-height: var(--ng-facts-line);
	text-decoration: none;
}

.ng-facts-place:hover {
	text-decoration: underline;
}

/* A series the catalogue could not link is still a series, and says so without
   promising a destination. */
.ng-facts-place--flat {
	color: var(--ng-fg);
}

.ng-facts-arrow {
	font-weight: 400;
	transition: transform 120ms ease;
}

.ng-facts-place:hover .ng-facts-arrow {
	transform: translateX(2px);
}

/*
 * The Style row: setting and perspective, muted, `·` between them. Deliberately
 * the quietest thing in the header. 15px, because --ng-muted is only used at
 * 14px and larger.
 */
.ng-quiet {
	display: block;
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.9375);
	line-height: var(--ng-facts-line);
}

.ng .ng-quiet a {
	color: var(--ng-muted);
}

.ng .ng-quiet a:hover {
	color: var(--ng-fg);
}

.ng-quiet-sep {
	margin: 0 calc(var(--ng-root) * 0.4);
}

/*
 * The 0.4.6 credit expander: a `<details>` of chips. The plugin's own template
 * no longer emits any of this, and these rules are here for a theme carrying an
 * override of single-game.php copied from that version, the way
 * `[data-ng-guide-group]` is still matched in expanders.js for the same reason.
 */
.ng-facts-more {
	flex: 0 0 auto;
	min-width: 0;
}

.ng-facts-more-summary {
	display: flex;
	align-items: center;
	min-height: var(--ng-facts-hit);
	margin: calc(var(--ng-root) * -0.95) 0;
	cursor: pointer;
	list-style: none;
}

.ng-facts-more-summary::-webkit-details-marker {
	display: none;
}

.ng-facts-more-pill {
	padding: calc(var(--ng-root) * 0.2) calc(var(--ng-root) * 0.6);
	background: var(--ng-panel-2);
	border: 1px solid var(--ng-control-border);
	border-radius: 999px;
	color: var(--ng-fg);
	font-size: calc(var(--ng-root) * 0.85);
	font-weight: 600;
	white-space: nowrap;
}

.ng-facts-rest {
	margin: 0;
}

.ng-hero-guides {
	margin: calc(var(--ng-root) * 1.25) 0 0;
}

.ng-stores {
	margin-top: calc(var(--ng-root) * 1.25);
}

.ng-stores-list {
	display: flex;
	flex-wrap: wrap;
	gap: calc(var(--ng-root) * 0.5);
	margin: 0;
	padding: 0;
	list-style: none;
}

/*
 * The audit measured this at 38px tall. Unlike the small pill controls
 * elsewhere on this page, a store link is meant to read as a real button, so
 * it grows to the 44px floor directly rather than through an invisible hit
 * area: min-height plus flex centring, not more vertical padding, so the
 * horizontal padding (and the button's width) is untouched.
 */
.ng-store {
	display: inline-flex;
	align-items: center;
	min-height: calc(var(--ng-root) * 2.75);
	padding: 0 calc(var(--ng-root) * 0.75);
	background: var(--ng-panel);
	border: 1px solid var(--ng-rule);
	border-radius: var(--ng-radius);
	font-size: calc(var(--ng-root) * 0.9);
	text-decoration: none;
}

.ng-store:hover {
	background: var(--ng-panel-2);
	text-decoration: underline;
}

/*
 * A database entry is not a storefront, so it is not a button: no panel, no
 * border, muted, underlined like the prose links it behaves like. 1,290 of the
 * 1,348 outbound links in this catalogue are igdb.com, so this is the common
 * case and .ng-store is the rare one. Both keep the 44px touch target, which
 * here comes from the same min-height and flex centring rather than from
 * padding: a link nobody can hit is no better for being honest.
 */
.ng-refs {
	margin-top: calc(var(--ng-root) * 0.75);
	font-size: calc(var(--ng-root) * 0.9);
}

.ng-refs-list {
	display: flex;
	flex-wrap: wrap;
	gap: calc(var(--ng-root) * 0.25) calc(var(--ng-root) * 1);
	margin: 0;
	padding: 0;
	list-style: none;
}

.ng-ref {
	display: inline-flex;
	align-items: center;
	min-height: calc(var(--ng-root) * 2.75);
	color: var(--ng-muted);
	text-decoration: underline;
}

.ng-ref:hover {
	color: var(--ng-fg);
}

.ng-jump {
	margin: calc(var(--ng-root) * 2) 0 0;
	padding: calc(var(--ng-root) * 0.75) calc(var(--ng-root) * 1);
	background: var(--ng-panel);
	border-radius: var(--ng-radius);
}

.ng-jump-list {
	display: flex;
	flex-wrap: wrap;
	gap: calc(var(--ng-root) * 0.5) calc(var(--ng-root) * 1.25);
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: calc(var(--ng-root) * 0.95);
}

.ng-jump-list a {
	display: inline-flex;
	align-items: baseline;
	gap: calc(var(--ng-root) * 0.4);
	text-decoration: none;
}

.ng-jump-list a:hover {
	text-decoration: underline;
}

/* ------------------------------------------------------- lists and cards ---- */

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

.ng-item {
	padding: calc(var(--ng-root) * 0.75) 0;
	border-bottom: 1px solid var(--ng-rule);
}

.ng-item:last-child {
	border-bottom: 0;
}

.ng-item-title {
	font-size: calc(var(--ng-root) * 1.05);
	font-weight: 600;
	text-decoration: none;
}

.ng-item-title:hover {
	text-decoration: underline;
}

.ng-item-meta {
	margin: calc(var(--ng-root) * 0.2) 0 0;
	color: var(--ng-muted);
	/* --ng-muted is only used at 14px and larger; 0.875 * the 16px root is
	   exactly that floor. */
	font-size: calc(var(--ng-root) * 0.875);
	line-height: 1.5;
}

/*
 * What kind of writing a row is: Review, News, Interview, Preview, Article,
 * Upcoming release. Same pill family as .ng-chip and .ng-count, on the second
 * panel tone so it reads as a label attached to the row rather than as another
 * chip a reader can follow. Deliberately not a link: a game page's rows already
 * link to the post, and a category archive is a different journey.
 *
 * 13px from the root, not 0.8125em: an `em` here would resolve against
 * .ng-item-meta's own 14px and land at 11.4px, under the 12px floor.
 * --ng-fg rather than the muted colour it sits beside, for the reason .ng-count
 * gives: a label under 14px does not get the muted value.
 */
.ng-kind {
	display: inline-block;
	margin-right: calc(var(--ng-root) * 0.4);
	padding: calc(var(--ng-root) * 0.05) calc(var(--ng-root) * 0.45);
	background: var(--ng-panel-2);
	border: 1px solid var(--ng-rule);
	border-radius: 999px;
	color: var(--ng-fg);
	font-size: calc(var(--ng-root) * 0.8125);
	font-weight: 600;
	letter-spacing: 0.01em;
	white-space: nowrap;
}

.ng-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(calc(var(--ng-root) * 9.5), 1fr));
	gap: calc(var(--ng-root) * 1.25);
	margin: 0;
	padding: 0;
	list-style: none;
}

.ng-card {
	min-width: 0;
}

.ng-card-link {
	display: flex;
	flex-direction: column;
	gap: calc(var(--ng-root) * 0.5);
	height: 100%;
	color: var(--ng-fg);
	text-decoration: none;
}

.ng-card-link:hover .ng-card-title {
	color: var(--ng-accent);
	text-decoration: underline;
}

.ng-card-art {
	display: block;
	aspect-ratio: 3 / 4;
	overflow: hidden;
	background: var(--ng-panel);
	border: 1px solid var(--ng-rule);
	border-radius: var(--ng-radius);
}

/*
 * The grid keeps a fixed 3 / 4 shape, unlike the single card above, because it
 * is what puts every title on a row at the same height: covers of three
 * different shapes on one row would stagger the titles down the page.
 *
 * The ratio is on the image and not inherited from the frame through
 * `height: 100%`, which is what this rule used to say and which never took
 * effect. A percentage height resolves against the parent's specified height,
 * and `.ng-card-art`'s height is specified `auto` with only `aspect-ratio`
 * giving it a used value, so `height: 100%` computed to `auto` and every cover
 * rendered at its intrinsic ratio. Measured on the catalogue's A page: a
 * 616x353 cover in a 170x227 frame came out 168x96, leaving 130px of empty
 * panel hanging below it.
 *
 * A census of the 1,404 covers readable on disk says what that cost: 1,032
 * (73.5%) are exactly 3 / 4 and were unaffected, 139 (9.9%) are taller and were
 * having their bottoms clipped by the frame's `overflow: hidden`, 5% of the
 * image at the median and 14% at the worst, and 233 (16.6%) are wider and were
 * leaving that hole. Stating the ratio here fixes all three: `contain` shows
 * every cover whole, a taller one gains a hairline band at the sides and a wider
 * one a letterbox above and below, both on the frame's own panel tone.
 */
.ng-card-img {
	display: block;
	width: 100%;
	aspect-ratio: 3 / 4;
	object-fit: contain;
}

/*
 * The audit measured this at 1.5:1 (--ng-rule on --ng-panel, a hairline
 * colour never meant to carry text): it read as an empty box, exactly the
 * "no image" complaint this fallback exists to avoid. --ng-accent on
 * --ng-accent-soft instead: a warm, tinted tile that reads as a deliberate
 * monogram rather than a missing cover, at 6.1:1, well clear of the 4.5:1
 * floor for this size of text.
 */
.ng-card-noart {
	display: grid;
	place-items: center;
	height: 100%;
	background: var(--ng-accent-soft);
	color: var(--ng-accent);
	font-size: calc(var(--ng-root) * 2.5);
	font-weight: 700;
}

.ng-card-title {
	display: block;
	font-size: calc(var(--ng-root) * 0.95);
	font-weight: 600;
	line-height: 1.35;
}

.ng-card-meta {
	display: block;
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.8);
	line-height: 1.4;
}

/* ------------------------------------------------------ archive controls ---- */

.ng-archive-head {
	margin-bottom: calc(var(--ng-root) * 1.5);
}

.ng-archive-intro {
	max-width: var(--ng-measure);
	margin: 0;
	color: var(--ng-muted);
}

.ng-az {
	margin: 0 0 calc(var(--ng-root) * 1.25);
}

.ng-az-list {
	display: flex;
	flex-wrap: wrap;
	gap: calc(var(--ng-root) * 0.3);
	margin: 0;
	padding: 0;
	list-style: none;
}

/*
 * The audit measured this at 36px tall. Like .ng-store above, this is a real
 * button (a jump link), not a delicate pill, so it grows to the 44px floor
 * directly: min-height plus flex centring rather than more padding, so the
 * grid of letters does not get visibly wider or unevenly spaced.
 */
.ng-az-link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: calc(var(--ng-root) * 2.75);
	min-height: calc(var(--ng-root) * 2.75);
	padding: 0 calc(var(--ng-root) * 0.45);
	border: 1px solid var(--ng-control-border);
	border-radius: var(--ng-radius);
	font-size: calc(var(--ng-root) * 0.9);
	font-variant-numeric: tabular-nums;
	text-align: center;
	text-decoration: none;
}

.ng-az-link:hover {
	background: var(--ng-panel-2);
	text-decoration: none;
}

.ng-az-link.is-current {
	background: var(--ng-accent);
	border-color: var(--ng-accent);
	color: var(--ng-accent-ink);
	font-weight: 700;
}

/* Letters with no games. Muted and borderless, so plainly not a link, but at
   full opacity: dimming it further would drop below AA, and a letter you cannot
   read is not a useful way of saying "nothing here". */
.ng-az-link--off {
	border-color: transparent;
	color: var(--ng-muted);
}

.ng-filters {
	margin: 0 0 calc(var(--ng-root) * 1.5);
	padding: calc(var(--ng-root) * 1);
	background: var(--ng-panel);
	border: 1px solid var(--ng-control-border);
	border-radius: var(--ng-radius);
}

.ng-filters-heading {
	margin: 0 0 calc(var(--ng-root) * 0.75);
	/* --ng-muted is only used at 14px and larger; 0.875 * the 16px root is
	   exactly that floor. Shared with the guide library's filter form, which
	   is why this moved up from 0.8 rather than only being fixed there. */
	font-size: calc(var(--ng-root) * 0.875);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--ng-muted);
}

.ng-filters-row {
	display: flex;
	flex-wrap: wrap;
	gap: calc(var(--ng-root) * 1);
	align-items: flex-end;
}

.ng-field {
	display: flex;
	flex-direction: column;
	gap: calc(var(--ng-root) * 0.3);
	margin: 0;
	min-width: 0;
	flex: 1 1 calc(var(--ng-root) * 11);
}

.ng-field--actions {
	flex: 0 0 auto;
	flex-direction: row;
	gap: calc(var(--ng-root) * 0.5);
	align-items: center;
}

.ng-label {
	color: var(--ng-muted);
	/* --ng-muted is only used at 14px and larger; 0.875 * the 16px root is
	   exactly that floor. Shared with the guide library's filter form, which
	   is why this moved up from 0.8 rather than only being fixed there. */
	font-size: calc(var(--ng-root) * 0.875);
}

.ng-select {
	max-width: 100%;
	padding: calc(var(--ng-root) * 0.45) calc(var(--ng-root) * 0.5);
	background: var(--ng-bg);
	border: 1px solid var(--ng-control-border);
	border-radius: var(--ng-radius);
	color: var(--ng-fg);
	font: inherit;
	font-size: calc(var(--ng-root) * 0.95);
}

.ng-results {
	display: flex;
	flex-wrap: wrap;
	gap: calc(var(--ng-root) * 0.5) calc(var(--ng-root) * 1);
	align-items: center;
	margin: 0 0 calc(var(--ng-root) * 1.25);
}

.ng-results-count {
	margin: 0;
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.9);
	font-variant-numeric: tabular-nums;
}

.ng-chips--active {
	margin: 0;
}

/*
 * WordPress's own pagination markup, toned to match. `Template\Library`
 * builds the same class names by hand for the guide library (its main query
 * is short-circuited, so `the_posts_pagination()` has nothing to paginate),
 * which is why this is not scoped to `.navigation.pagination` alone.
 */
.ng .navigation.pagination {
	margin-top: calc(var(--ng-root) * 2.5);
}

.ng .nav-links {
	display: flex;
	flex-wrap: wrap;
	gap: calc(var(--ng-root) * 0.4);
}

.ng .page-numbers {
	display: inline-block;
	padding: calc(var(--ng-root) * 0.35) calc(var(--ng-root) * 0.7);
	border: 1px solid var(--ng-control-border);
	border-radius: var(--ng-radius);
	font-size: calc(var(--ng-root) * 0.9);
	text-decoration: none;
}

.ng .page-numbers.current {
	background: var(--ng-accent);
	border-color: var(--ng-accent);
	color: var(--ng-accent-ink);
	font-weight: 700;
}

/* The elision ellipsis: not a control, so no edge to draw. */
.ng .page-numbers.dots {
	border-color: transparent;
	padding-left: 0;
	padding-right: 0;
	color: var(--ng-muted);
}

/* --------------------------------------------------- guide library groups -- */

.ng-guide-groups {
	margin: 0;
	padding: 0;
	list-style: none;
}

.ng-guide-group-item {
	border-bottom: 1px solid var(--ng-rule);
}

.ng-guide-group-item:first-child {
	border-top: 1px solid var(--ng-rule);
}

/*
 * The whole row is the disclosure control. A native `<details>`/`<summary>`
 * already has an accessible name (its text content) and default keyboard
 * behaviour (Enter and Space toggle it, Tab reaches it in document order), so
 * there is nothing to add here but layout, the 44px tap-target floor and a
 * chevron that rotates on `[open]`. `assets/js/expanders.js` keeps an
 * explicit `aria-expanded` on the summary in sync with `[open]` on the
 * `toggle` event; the markup always starts closed with `aria-expanded="false"`,
 * so the state is correct before the script has run at all.
 */
.ng-guide-group-summary {
	display: flex;
	align-items: center;
	gap: calc(var(--ng-root) * 0.6);
	min-height: calc(var(--ng-root) * 2.75);
	padding: calc(var(--ng-root) * 0.4) 0;
	cursor: pointer;
	list-style: none;
}

.ng-guide-group-summary::-webkit-details-marker {
	display: none;
}

.ng-guide-group-chevron {
	flex: 0 0 auto;
	width: calc(var(--ng-root) * 0.55);
	height: calc(var(--ng-root) * 0.55);
	border-right: 2px solid var(--ng-control-border);
	border-bottom: 2px solid var(--ng-control-border);
	transform: rotate(-45deg);
	transition: transform 120ms ease;
}

@media (prefers-reduced-motion: reduce) {
	.ng-guide-group-chevron {
		transition: none;
	}
}

.ng-guide-group[open] > .ng-guide-group-summary .ng-guide-group-chevron {
	transform: rotate(45deg);
}

.ng-guide-group-title {
	flex: 1 1 auto;
	min-width: 0;
	font-weight: 600;
}

.ng-guide-group-count {
	flex: 0 0 auto;
	color: var(--ng-muted);
	/* --ng-muted is only used at 14px and larger; 0.875 * the 16px root is
	   exactly that floor. */
	font-size: calc(var(--ng-root) * 0.875);
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}

.ng-guide-group-body {
	padding: 0 0 calc(var(--ng-root) * 1) calc(var(--ng-root) * 1.2);
}

.ng-guide-group-gamelink {
	margin: 0 0 calc(var(--ng-root) * 0.75);
	font-size: calc(var(--ng-root) * 0.9);
}

.ng-list--grouped {
	margin: 0;
	padding: 0;
	list-style: none;
}

.ng-list--grouped > .ng-item {
	padding: calc(var(--ng-root) * 0.5) 0;
	border-top: 1px solid var(--ng-rule);
}

.ng-list--grouped > .ng-item:first-child {
	border-top: 0;
}

/* The "needs a game link" editor list: visually set apart with a dashed rule
   rather than a colour that would need its own contrast check, and its title
   reads muted so it does not compete with the guides above it for attention. */
.ng-guide-group--editor {
	margin-top: calc(var(--ng-root) * 2);
	padding-top: calc(var(--ng-root) * 1);
	border-top: 1px dashed var(--ng-rule);
}

.ng-guide-group--editor .ng-guide-group-title {
	color: var(--ng-muted);
}

/* --------------------------------------------- walkthrough page furniture -- */

/* Fixed, so it cannot be inside `.ng`. Hidden until the script announces
   itself: a progress bar frozen at zero is worse than no progress bar. */
.ng-progress {
	display: none;
}

html.ng-js .ng-progress {
	position: fixed;
	top: 0;
	right: 0;
	left: 0;
	z-index: 60;
	display: block;
	height: 3px;
	background: transparent;
	pointer-events: none;
}

.ng-progress-bar {
	width: 100%;
	height: 100%;
	background: var(--ng-accent);
	transform: scaleX(var(--ng-progress, 0));
	transform-origin: 0 50%;
	transition: transform 80ms linear;
}

/*
 * The game card beside a walkthrough (single-walkthrough.php's aside, above
 * the contents list). Replaces the old sticky horizontal game bar: that bar
 * showed a thumbnail and "Walkthrough for <title>" only, which this card
 * covers along with year, developer, platform chips, a store link and the
 * other-guides count, so the bar would have been showing a subset of this
 * card's own facts for no reason.
 */
.ng-gamecard {
	/* Art plus the padding on either side of it, so the panel is exactly as
	   wide as the cover it frames and stays a card when the aside stops being
	   a column. The contents list beside it is deliberately not capped: a list
	   of links wants the full width, a poster does not. */
	max-width: calc(var(--ng-cover-max) + var(--ng-root) * 2);
	margin: 0 0 calc(var(--ng-root) * 1.5);
	padding: calc(var(--ng-root) * 1);
	background: var(--ng-panel);
	border: 1px solid var(--ng-rule);
	border-radius: var(--ng-radius);
}

.ng-gamecard-link {
	display: block;
	color: var(--ng-fg);
	text-decoration: none;
}

/*
 * The cover keeps its own shape here. A census of the 1,403 stored covers that
 * are readable on disk says 1,283 are portrait, and the median ratio is exactly
 * 0.750, so a 3 / 4 box is right for most of the catalogue; but 109 are
 * landscape and 68 of those are wider than 1.5:1, and forcing one of those into
 * a 3 / 4 box with `object-fit: cover` showed a zoomed centre slice of a banner.
 * The filename is no help in telling the two apart: 1,259 of the 1,356 files
 * named `-header.*` are portrait, so the suffix says where the image came from
 * and nothing about its shape.
 *
 * A single image with nothing to line up against does not need a fixed box, so
 * it does not get one: the intrinsic ratio is kept, bounded on both axes. The
 * height cap is a 3 / 4 poster at the maximum width, which means no poster is
 * ever letterboxed and only a taller-than-poster outlier (the tallest cover in
 * the catalogue is 0.646) is bounded at all. `contain` and the panel tone are
 * for that case: a letterbox on the card's own surface, never a crop.
 */
.ng-gamecard-img {
	display: block;
	width: 100%;
	max-width: var(--ng-cover-max);
	height: auto;
	max-height: calc(var(--ng-cover-max) * 4 / 3);
	margin: 0 0 calc(var(--ng-root) * 0.75);
	background: var(--ng-panel-2);
	object-fit: contain;
	border-radius: var(--ng-radius);
}

.ng-gamecard-noart {
	display: grid;
	place-items: center;
	aspect-ratio: 3 / 4;
	margin: 0 0 calc(var(--ng-root) * 0.75);
	background: var(--ng-panel-2);
	border-radius: var(--ng-radius);
	color: var(--ng-rule);
	font-size: calc(var(--ng-root) * 2.5);
	font-weight: 700;
}

.ng-gamecard-title {
	display: block;
	font-size: calc(var(--ng-root) * 1.1);
	font-weight: 700;
	line-height: 1.3;
}

.ng-gamecard-link:hover .ng-gamecard-title {
	color: var(--ng-accent);
	text-decoration: underline;
}

.ng-gamecard-meta {
	margin: calc(var(--ng-root) * 0.4) 0 0;
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.9);
}

.ng-gamecard-store {
	margin: calc(var(--ng-root) * 0.75) 0 0;
}

.ng-gamecard-guides {
	margin: calc(var(--ng-root) * 0.75) 0 0;
	padding-top: calc(var(--ng-root) * 0.75);
	border-top: 1px solid var(--ng-rule);
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.9);
}

.ng-head {
	margin: 0 0 calc(var(--ng-root) * 1.5);
}

.ng-head-meta {
	display: flex;
	flex-wrap: wrap;
	gap: calc(var(--ng-root) * 0.3) calc(var(--ng-root) * 1);
	margin: 0;
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.9);
}

.ng-head-by {
	color: var(--ng-fg);
	font-weight: 600;
}

/* Provenance badge. Reuses .ng-badge for the pill itself; this adds only the
   line it sits on above the headline, and the one distinction worth drawing:
   a guide written for this site is filled, an imported one is outlined. Both
   keep .ng-badge's 12px floor and its accent-on-accent-soft pairing. */
.ng-origin {
	margin: 0 0 calc(var(--ng-root) * 0.4);
}

.ng-badge--origin {
	text-transform: none;
	letter-spacing: 0.02em;
}

.ng-badge--ja-original {
	background: var(--ng-accent);
	border-color: var(--ng-accent);
	color: var(--ng-accent-ink);
}

/* A title attribute is the only affordance on the badge, so say so: a cursor
   that does not change makes the tooltip undiscoverable. */
.ng-badge--origin[title] {
	cursor: help;
}

/* ------------------------------------------------------------- credit ---- */

.ng-credit {
	max-width: var(--ng-measure);
	margin: 0 0 calc(var(--ng-root) * 2);
	padding: calc(var(--ng-root) * 1) calc(var(--ng-root) * 1.15);
	background: var(--ng-panel);
	border-left: 3px solid var(--ng-accent);
	border-radius: var(--ng-radius);
}

.ng-credit-heading {
	margin: 0;
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.75);
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

.ng-credit-name {
	margin: calc(var(--ng-root) * 0.2) 0 0;
	font-size: calc(var(--ng-root) * 1.2);
	font-weight: 700;
	line-height: 1.3;
}

.ng-credit-note,
.ng-credit-origin,
.ng-credit-notice,
.ng-credit-thanks {
	margin: calc(var(--ng-root) * 0.5) 0 0;
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.88);
	line-height: 1.55;
}

.ng-credit-thanks {
	font-size: calc(var(--ng-root) * 0.8);
	font-style: italic;
}

.ng-credit-action {
	margin: calc(var(--ng-root) * 0.75) 0 0;
}

/* No byline recorded. Same weight of component, not a hole in the page: the
   accent border stays, the copy explains, nothing says "Anonymous". */
.ng-credit--uncredited {
	border-left-color: var(--ng-rule);
}

.ng-credit--uncredited .ng-credit-heading {
	color: var(--ng-fg);
	font-size: calc(var(--ng-root) * 0.95);
	letter-spacing: 0;
	text-transform: none;
}

/* ------------------------------------------------- layout and contents ---- */

.ng-layout {
	min-width: 0;
}

.ng-main {
	/* Without this a wide `pre` inside a grid item blows the column out and
	   the page scrolls sideways: `minmax(0, 1fr)` alone is not enough. */
	min-width: 0;
}

/*
 * "In this guide": the derived outline, the in-guide filter and step progress,
 * one panel under the game card. Below 960px the whole thing becomes a sheet
 * docked to the bottom of the viewport whose summary is the handle, which is
 * covered in the small-screen block at the end of this file.
 */
.ng-toc {
	margin: 0 0 calc(var(--ng-root) * 1.5);
	padding: calc(var(--ng-root) * 0.5) calc(var(--ng-root) * 0.9);
	background: var(--ng-panel);
	border: 1px solid var(--ng-rule);
	border-radius: var(--ng-radius);
}

.ng-toc-summary {
	display: flex;
	align-items: baseline;
	gap: calc(var(--ng-root) * 0.5);
	/* 44px including the padding: on a phone this summary is the handle of the
	   sheet and the only way back into the outline. */
	min-height: calc(var(--ng-root) * 2.75);
	padding: calc(var(--ng-root) * 0.55) 0;
	color: var(--ng-muted);
	cursor: pointer;
	font-size: calc(var(--ng-root) * 0.78);
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

.ng-toc-summary-label {
	flex: 0 0 auto;
}

/* The section the reader is in, written into the handle by the script. Empty
   until then, and empty on a wide screen where the summary is not shown. */
.ng-toc-here {
	flex: 1 1 auto;
	overflow: hidden;
	color: var(--ng-fg);
	font-size: calc(var(--ng-root) * 0.875);
	font-weight: 400;
	letter-spacing: 0;
	text-overflow: ellipsis;
	text-transform: none;
	white-space: nowrap;
}

/* ---- the filter ---- */

/* Hidden without the script: a search box that does nothing is a lie about the
   page. The outline itself is server-rendered and works either way. */
.ng-toc-find {
	display: none;
}

html.ng-js .ng-toc-find {
	display: block;
	margin: calc(var(--ng-root) * 0.35) 0 calc(var(--ng-root) * 0.6);
}

.ng-toc-input {
	box-sizing: border-box;
	width: 100%;
	/* 16px, and not one pixel less: below that iOS zooms the whole page when
	   the field takes focus. */
	min-height: calc(var(--ng-root) * 2.75);
	padding: calc(var(--ng-root) * 0.45) calc(var(--ng-root) * 0.6);
	background: var(--ng-bg);
	border: 1px solid var(--ng-control-border);
	border-radius: var(--ng-radius);
	color: var(--ng-fg);
	font-family: inherit;
	font-size: var(--ng-root);
}

.ng-toc-input::placeholder {
	color: var(--ng-muted);
	opacity: 1;
}

.ng-toc-input:focus {
	outline: 2px solid var(--ng-accent);
	outline-offset: 1px;
}

.ng-toc-found:empty {
	display: none;
}

/* The Enter behaviour, explained where it is about to be used and nowhere else.
   Visually hidden rather than `display: none`, so it is still read out by the
   `aria-describedby` on the field. */
.ng-toc-hint {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
}

.ng-toc-input:focus ~ .ng-toc-hint {
	position: static;
	width: auto;
	height: auto;
	margin: calc(var(--ng-root) * 0.4) 0 0;
	overflow: visible;
	clip: auto;
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.875);
	line-height: 1.4;
	white-space: normal;
}

.ng-toc-found,
.ng-toc-empty,
.ng-toc-note {
	margin: calc(var(--ng-root) * 0.4) 0 0;
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.875);
	line-height: 1.4;
}

/* What the in-guide find lands on. A background rather than a colour change, so
   the marked text keeps the body's own contrast. */
.ng-hit {
	background: var(--ng-accent-soft);
	border-bottom: 2px solid var(--ng-accent);
	color: inherit;
}

/* ---- step progress ---- */

.ng-progress-steps {
	margin: calc(var(--ng-root) * 0.5) 0 calc(var(--ng-root) * 0.75);
	padding-bottom: calc(var(--ng-root) * 0.6);
	border-bottom: 1px solid var(--ng-rule);
}

.ng-progress-steps-line {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: calc(var(--ng-root) * 0.5);
	margin: 0 0 calc(var(--ng-root) * 0.4);
	font-size: calc(var(--ng-root) * 0.875);
}

.ng-progress-steps-count {
	color: var(--ng-fg);
	font-variant-numeric: tabular-nums;
}

.ng-progress-steps-reset {
	min-height: calc(var(--ng-root) * 2.75);
	padding: 0 calc(var(--ng-root) * 0.6);
	background: none;
	border: 1px solid var(--ng-control-border);
	border-radius: var(--ng-radius);
	color: var(--ng-accent);
	cursor: pointer;
	font-family: inherit;
	font-size: calc(var(--ng-root) * 0.8);
}

.ng-progress-steps-track {
	height: 4px;
	overflow: hidden;
	background: var(--ng-bg);
	border-radius: 2px;
}

.ng-progress-steps-fill {
	width: 100%;
	height: 100%;
	background: var(--ng-accent);
	transform: scaleX(var(--ng-steps, 0));
	transform-origin: 0 50%;
	transition: transform 120ms linear;
}

/* ---- the outline ---- */

/*
 * Numbered by CSS counter rather than by the list marker. The level-3 entries
 * live in the same flat list and a marker would number them too: the outline
 * would read 1, 2, 3 across a major section and its two sub-sections. The
 * counter is incremented by the level-2 entries only, and the sub-entries are
 * shown by indentation.
 */
.ng-toc-list {
	counter-reset: ng-toc;
	margin: calc(var(--ng-root) * 0.4) 0 calc(var(--ng-root) * 0.5);
	padding: 0;
	list-style: none;
	font-size: calc(var(--ng-root) * 0.875);
	line-height: 1.45;
}

.ng-toc-item {
	display: flex;
	align-items: baseline;
	gap: calc(var(--ng-root) * 0.4);
	margin: 0;
}

/* An author `display` beats the user agent's `[hidden] { display: none }`, so
   the filter's own hiding has to be said here or typing into it does nothing. */
.ng-toc-item[hidden] {
	display: none;
}

.ng-toc-item--h2::before {
	content: counter(ng-toc) ".";
	flex: 0 0 auto;
	color: var(--ng-muted);
	font-variant-numeric: tabular-nums;
}

.ng-toc-item--h2 {
	counter-increment: ng-toc;
}

.ng-toc-item--h3 {
	padding-left: calc(var(--ng-root) * 1.1);
	color: var(--ng-muted);
}

.ng-toc-link {
	/* 44px of vertical target on a phone, where this list is a sheet and every
	   entry is a tap. On a mouse it reads as comfortable line spacing. */
	flex: 1 1 auto;
	min-height: calc(var(--ng-root) * 1.75);
	padding: calc(var(--ng-root) * 0.4) 0;
	text-decoration: none;
}

.ng-toc-link:hover {
	text-decoration: underline;
}

/* Set by the script as the reader passes each section. */
.ng-toc-link.is-current {
	color: var(--ng-fg);
	font-weight: 700;
}

/* A section on another page of the same guide: still a link, marked so the jump
   is not a surprise. */
.ng-toc-page {
	flex: 0 0 auto;
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.875);
	font-variant-numeric: tabular-nums;
}

/* ---- tickable steps ---- */

/*
 * A step's checkbox sits at the head of the step's first line, in the text.
 *
 * A gutter was the first design and this archive does not survive it: a
 * converted guide writes consecutive steps as `<br />`-separated lines inside
 * one paragraph, and several floats hanging off one block pack side by side
 * rather than one per line, which put checkboxes on top of the prose. In the
 * flow there is nothing to collide with.
 *
 * The box is 24px, which is the target-size floor, and the empty `<label>`
 * beside it is stretched to 44px on a touch screen below. It is absolutely
 * positioned, so the 44px costs no layout: the line keeps its 24px.
 *
 * Hidden until the script announces itself: a checkbox that forgets everything
 * on the next page view is worse than no checkbox.
 *
 * Steps are never rendered inside a preserved block at all. A checkbox in a
 * column-aligned ASCII map moves the columns, which is the one thing 721 guides
 * are preserved byte for byte to prevent.
 */
.ng-step {
	display: none;
}

html.ng-js .ng-step {
	position: relative;
	display: inline-block;
	width: calc(var(--ng-root) * 1.125);
	height: calc(var(--ng-root) * 1.125);
	margin-right: calc(var(--ng-root) * 0.5);
	vertical-align: -0.12em;
}

/*
 * Drawn by this stylesheet, not the UA. The native checkbox is a control
 * painted for a white page: on this palette it lands as a bright white
 * square, wider than the text it belongs to, in every line of a 75-step
 * guide. `appearance: none` trades the UA drawing for the panel and border
 * treatment every other control on the page already wears, at text height
 * rather than over it. The tap target is unchanged: `.ng-step-hit` is out of
 * flow and keeps its own size, so the box shrinking never shrinks the thing
 * a thumb aims at.
 */
.ng-step-box {
	width: 100%;
	height: 100%;
	margin: 0;
	appearance: none;
	-webkit-appearance: none;
	background: var(--ng-panel-2);
	border: 1px solid var(--ng-control-border);
	border-radius: 3px;
	cursor: pointer;
}

.ng-step-box:hover {
	border-color: var(--ng-accent);
}

/*
 * The tick is an inline SVG rather than a glyph so its weight and centring do
 * not depend on the font stack. Its stroke is --ng-accent-ink's value written
 * out (#1a1816): a data URI cannot read a custom property, and the ink over
 * the accent fill is the same pairing the accent buttons already use.
 */
.ng-step-box:checked {
	background-color: var(--ng-accent);
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%231a1816' stroke-width='2.75' stroke-linecap='round' stroke-linejoin='round' d='M3.25 8.5l3.25 3.25 6.25-7.5'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: center;
	background-size: 82%;
	border-color: var(--ng-accent);
}

/* Inputs are not in the shared focus rule at the top of the file, which
   covers links, buttons, selects and summaries; same treatment, added here. */
.ng-step-box:focus {
	outline: 2px solid var(--ng-accent);
	outline-offset: 2px;
}

.ng-step-box:focus:not(:focus-visible) {
	outline: none;
}

/* The tap target. Out of flow, so widening it never moves a word of the guide. */
.ng-step-hit {
	position: absolute;
	top: 50%;
	left: 50%;
	width: calc(var(--ng-root) * 1.5);
	height: calc(var(--ng-root) * 1.5);
	transform: translate(-50%, -50%);
	cursor: pointer;
}

/*
 * A step this reader has ticked. Muted rather than struck through: the text is
 * still the guide, and `--ng-muted` is 5.96:1, which is AA at this size.
 *
 * `!important` because the legacy inline-style neutraliser above is `!important`
 * on every text element inside `.ng-prose`, including the `li` a step usually
 * lands in. Without this the checkbox ticked and nothing else changed.
 */
.ng-prose .ng-step-done,
.ng-prose .ng-step-done > * {
	color: var(--ng-muted) !important;
}

/* ---- page navigation for a long guide ---- */

.ng-guidepager {
	margin: calc(var(--ng-root) * 2.5) 0 0;
	padding-top: calc(var(--ng-root) * 1.25);
	border-top: 1px solid var(--ng-rule);
}

.ng-guidepager-links {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: calc(var(--ng-root) * 0.75);
}

.ng-guidepager-numbers {
	display: flex;
	flex-wrap: wrap;
	gap: calc(var(--ng-root) * 0.4);
	margin: 0;
	padding: 0;
	list-style: none;
}

.ng-guidepager-number {
	margin: 0;
}

.ng-guidepager-num,
.ng-guidepager-step {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: calc(var(--ng-root) * 0.35);
	min-width: calc(var(--ng-root) * 2.75);
	min-height: calc(var(--ng-root) * 2.75);
	padding: 0 calc(var(--ng-root) * 0.65);
	border: 1px solid var(--ng-control-border);
	border-radius: var(--ng-radius);
	font-size: calc(var(--ng-root) * 0.95);
	font-variant-numeric: tabular-nums;
	text-decoration: none;
}

.ng-guidepager-num.is-current {
	background: var(--ng-accent);
	border-color: var(--ng-accent);
	color: var(--ng-accent-ink);
	font-weight: 700;
}

.ng-guidepager-single {
	margin: calc(var(--ng-root) * 0.9) 0 0;
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.875);
}

/* ---- the raw view, and the link to it ---- */

.ng-rawlink {
	margin: calc(var(--ng-root) * 1.5) 0 0;
	font-size: calc(var(--ng-root) * 0.875);
}

.ng-notice--raw {
	max-width: var(--ng-measure);
	margin: 0 0 calc(var(--ng-root) * 1.5);
	padding: calc(var(--ng-root) * 0.75) calc(var(--ng-root) * 1);
	background: var(--ng-panel);
	border-left: 3px solid var(--ng-accent-soft);
	border-radius: var(--ng-radius);
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.875);
}

/* An injected section anchor. It has no size of its own and must not add any:
   the scroll offset is what keeps a deep link from landing under the sticky
   chrome. */
.ng-anchor {
	display: inline;
	scroll-margin-top: calc(var(--ng-root) * 5);
}

.ng-anchor:focus {
	outline: none;
}

/* ---- the sibling guides, grouped by what each one is ---- */

.ng-guidekind + .ng-guidekind {
	margin-top: calc(var(--ng-root) * 1.25);
}

.ng-guidekind-heading {
	display: flex;
	align-items: baseline;
	gap: calc(var(--ng-root) * 0.5);
	margin: 0 0 calc(var(--ng-root) * 0.5);
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.8);
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

.ng-guidekind-count {
	color: var(--ng-fg);
	font-variant-numeric: tabular-nums;
	letter-spacing: 0;
}

.ng-partnav {
	display: flex;
	flex-wrap: wrap;
	gap: calc(var(--ng-root) * 1);
	justify-content: space-between;
	margin: calc(var(--ng-root) * 2.5) 0 0;
	padding-top: calc(var(--ng-root) * 1.25);
	border-top: 1px solid var(--ng-rule);
}

.ng-partnav-link {
	flex: 1 1 calc(var(--ng-root) * 12);
	padding: calc(var(--ng-root) * 0.75) calc(var(--ng-root) * 1);
	background: var(--ng-panel);
	border: 1px solid var(--ng-rule);
	border-radius: var(--ng-radius);
	text-decoration: none;
}

.ng-partnav-link--next {
	text-align: right;
}

.ng-partnav-label {
	display: block;
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.75);
	letter-spacing: 0.06em;
	text-transform: uppercase;
}

.ng-partnav-title {
	display: block;
	color: var(--ng-accent);
	font-weight: 600;
}

/* ---- the pages of one guide, as a sequence ---- */

/*
 * Deliberately unlike every other list of guides on the page. Those run down a
 * column, one row per guide, ruled apart, because they are alternatives and a
 * reader is picking one. These are chapters, so they run along a line with an
 * arrow between them, which is the same shape a breadcrumb trail has and reads
 * the same way: this, then this, then this.
 *
 * The arrow is generated content on the second and later items, so it is not in
 * the markup, cannot be selected with the text, and disappears with the
 * stylesheet, leaving a plain ordered list.
 */
.ng-partseq {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 0 calc(var(--ng-root) * 0.5);
	margin: calc(var(--ng-root) * 0.5) 0 0;
	padding: 0;
	list-style: none;
}

.ng-partseq-item {
	display: flex;
	align-items: baseline;
	gap: calc(var(--ng-root) * 0.5);
	/* No rule and no padding of its own: an item is a word in a line here, not
	   a row in a table. */
	font-size: calc(var(--ng-root) * 0.95);
}

.ng-partseq-item + .ng-partseq-item::before {
	color: var(--ng-muted);
	content: "\2192";
	/* --ng-muted is only used at 14px and larger, and this is the same 15.2px
	   the labels beside it are set at. */
	font-size: 1em;
}

/*
 * 44px of tap area without 44px of line. The link's own text sits on one
 * reading line, and the padding that makes it a target is put back with a
 * negative margin, the same trick .ng-step-hit uses beside a step checkbox.
 */
.ng-partseq-link,
.ng-partseq-here {
	display: inline-flex;
	align-items: center;
	min-height: calc(var(--ng-root) * 2.75);
	margin: calc(var(--ng-root) * -0.6) 0;
	padding: calc(var(--ng-root) * 0.6) 0;
}

.ng-partseq-link {
	text-decoration: none;
}

.ng-partseq-link:hover {
	text-decoration: underline;
}

.ng-partseq-here {
	color: var(--ng-fg);
	font-weight: 700;
}

/* The page you are on, marked without colour alone: an underline below the word,
   in the accent, so it reads as "here" in a screenshot and in high contrast. */
.ng-partseq-item--current {
	text-decoration: underline;
	text-decoration-color: var(--ng-accent);
	text-decoration-thickness: 2px;
	text-underline-offset: 4px;
}

/* The line above the sequence on a guide's own page: "One guide by Eddy, in 3
   parts. You are reading part 2." */
.ng-parts-note {
	margin: 0;
	color: var(--ng-muted);
	/* --ng-muted at 14.4px, over the 14px floor it is verified for. */
	font-size: calc(var(--ng-root) * 0.9);
	line-height: 1.5;
}

/* A row that is a whole guide rather than one page of one. The sequence
   underneath is indented so it reads as belonging to the row above it. */
.ng-item--work > .ng-partseq {
	margin-left: calc(var(--ng-root) * 0.75);
	padding-left: calc(var(--ng-root) * 0.75);
	border-left: 2px solid var(--ng-rule);
}

/* Whatever stood where a dead image did, when its alt text carried words.
   Prose, not a placeholder box: there is no picture to make room for. */
.ng-lostimage {
	color: var(--ng-muted);
	/* --ng-muted at 15.2px. */
	font-size: calc(var(--ng-root) * 0.95);
	font-style: italic;
}

/* ------------------------------------------------------ contributor page ---- */

.ng-contributor-head {
	max-width: var(--ng-measure);
	margin: 0 0 calc(var(--ng-root) * 2);
}

.ng-stats {
	display: flex;
	flex-wrap: wrap;
	gap: calc(var(--ng-root) * 1.5);
	margin: calc(var(--ng-root) * 1.25) 0;
	padding: calc(var(--ng-root) * 1) 0;
	border-top: 1px solid var(--ng-rule);
	border-bottom: 1px solid var(--ng-rule);
	list-style: none;
}

.ng-stat {
	display: flex;
	flex-direction: column;
}

.ng-stat-value {
	font-size: calc(var(--ng-root) * 1.5);
	font-weight: 700;
	font-variant-numeric: tabular-nums;
	line-height: 1.1;
}

.ng-stat-label {
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.8);
	letter-spacing: 0.04em;
	text-transform: uppercase;
}

.ng-contributor-thanks {
	color: var(--ng-muted);
	font-size: calc(var(--ng-root) * 0.9);
}

/* ---------------------------------------------------------- wide screens ---- */

@media (min-width: 704px) {
	.ng-hero {
		grid-template-columns: calc(var(--ng-root) * 14) minmax(0, 1fr);
		align-items: start;
		gap: calc(var(--ng-root) * 2);
	}

	.ng-hero-art {
		max-width: none;
	}
}

@media (min-width: 960px) {
	/*
	 * A left rail, which is what the design asks for and what the source order
	 * already was: the aside is written before the prose, so the reader who gets
	 * no CSS at all still meets "which game is this" and "what is in this guide"
	 * before the guide. A right rail was the 0.2.x layout and needed the grid to
	 * reverse the two.
	 */
	.ng-layout--sided {
		display: grid;
		grid-template-columns: calc(var(--ng-root) * 15) minmax(0, 1fr);
		gap: calc(var(--ng-root) * 2.5);
		align-items: start;
	}

	.ng-layout--sided .ng-side {
		grid-column: 1;
		grid-row: 1;
		/* The whole rail stays put while the prose scrolls, card and contents
		   list together: this is what used to be the sticky game bar's job,
		   now done by the panel that replaced it. Scrolls internally rather
		   than growing past the viewport, for a guide with 88 sections. */
		position: sticky;
		top: calc(var(--ng-root) * 4.5);
		max-height: calc(100vh - calc(var(--ng-root) * 6));
		overflow-y: auto;
		overscroll-behavior: contain;
	}

	.ng-layout--sided .ng-main {
		grid-column: 2;
		grid-row: 1;
	}

	/* On a wide screen the list is simply there: the disclosure control would
	   be furniture. It stays open (the script only collapses it on narrow
	   viewports) and the fallback below covers the case where it does not. */
	.ng-toc > .ng-toc-summary {
		display: none;
	}

	.ng-toc:not([open]) > .ng-toc-body {
		display: block;
	}
}

/* ------------------------------------------------------- contents sheet ---- */

/*
 * Below the rail breakpoint the outline docks to the bottom of the viewport and
 * its summary becomes the handle, naming the section the reader is in. That is
 * the one piece of navigation on this page worth a permanent 44px of a phone
 * screen: on an 88-section guide the alternative is scrolling back to the top of
 * a 277k document to find out where you are.
 *
 * Only with the script, for two reasons: the handle is empty without it, and a
 * panel fixed over the page that can only be closed by a `<details>` toggle is
 * worse than an ordinary block if that toggle is the only thing keeping it out
 * of the way. The `px` unit matches the wide-screen query above rather than the
 * `rem` it used to use, because `rem` is the document root and on this site the
 * theme sets that to 10px, which turned a 960px breakpoint into a 600px one.
 */
@media (max-width: 959.98px) {
	html.ng-js .ng-toc {
		position: fixed;
		right: 0;
		bottom: 0;
		left: 0;
		z-index: 55;
		max-height: 70vh;
		margin: 0;
		padding: 0 clamp(calc(var(--ng-root) * 1), 4vw, calc(var(--ng-root) * 2));
		overflow-y: auto;
		overscroll-behavior: contain;
		background: var(--ng-panel-2);
		border-width: 1px 0 0;
		border-radius: 0;
		box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.45);
	}

	/* Room for the sheet's own handle, so the last line of the guide is not
	   underneath it. */
	html.ng-js .ng-walkthrough {
		padding-bottom: calc(var(--ng-root) * 5);
	}

	html.ng-js .ng-toc-body {
		padding-bottom: calc(var(--ng-root) * 1);
	}

	/* 44px of tap target on a step, without 44px of line. The label overlaps the
	   first word or two of the step, which on a touch screen is a feature: the
	   whole start of the row toggles it. */
	.ng-step-hit {
		width: calc(var(--ng-root) * 2.75);
		height: calc(var(--ng-root) * 2.75);
	}
}

/* --------------------------------------------------------- small screens ---- */

@media (max-width: 480px) {
	/*
	 * The label goes above its values. A 112px label column beside a 375px
	 * screen leaves 200px for the names, which is one chip per line for
	 * anything called "Nintendo Switch".
	 */
	.ng-facts-row {
		grid-template-columns: minmax(0, 1fr);
		row-gap: calc(var(--ng-root) * 0.15);
	}

	.ng-facts {
		gap: calc(var(--ng-root) * 0.75);
	}

	.ng {
		--ng-size: calc(var(--ng-root) * 1.0625); /* 17px: 19px at 375px is a 32-character line */
		padding-bottom: calc(var(--ng-root) * 3);
	}

	.ng-grid {
		grid-template-columns: repeat(auto-fill, minmax(calc(var(--ng-root) * 7.5), 1fr));
		gap: calc(var(--ng-root) * 1);
	}

	.ng-partnav-link--next {
		text-align: left;
	}

	/* 44px of tap target each, on the screen where they are tapped. */
	.ng-toc-link {
		min-height: calc(var(--ng-root) * 2.75);
		padding: calc(var(--ng-root) * 0.65) 0;
	}
}

/* ------------------------------------------------------- reduced motion ---- */

@media (prefers-reduced-motion: reduce) {
	.ng-progress-bar {
		transition: none;
	}

	.ng *,
	.ng *::before,
	.ng *::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}
}
