/*
 * Nivoli JA rules. Tokens only: no literal colour and no literal font size
 * below this line, and no `rem` anywhere. See assets/css/tokens.css.
 */

*, *::before, *::after { box-sizing: border-box; }

html { font-size: 100%; }

body {
	margin: 0;
	background: var(--ja-bg);
	color: var(--ja-ink);
	font-family: var(--ja-sans);
	font-size: var(--ja-size-body);
	line-height: var(--ja-leading);
	-webkit-text-size-adjust: 100%;
}

img, video { max-width: 100%; height: auto; }

a { color: var(--ja-accent); text-underline-offset: 0.15em; }
a:hover { text-decoration-thickness: 2px; }

:focus-visible { outline: 2px solid var(--ja-accent); outline-offset: 2px; border-radius: 2px; }

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

.ja-skip {
	position: absolute; left: -9999px;
	background: var(--ja-accent); color: var(--ja-accent-ink);
	padding: calc(var(--ja-root) * 0.5) calc(var(--ja-root) * 0.75);
}
.ja-skip:focus { left: calc(var(--ja-root) * 0.5); top: calc(var(--ja-root) * 0.5); z-index: 10; }

.ja-shell {
	max-width: var(--ja-shell);
	margin: 0 auto;
	padding: 0 clamp(calc(var(--ja-root) * 1), 4vw, calc(var(--ja-root) * 2));
}

/* masthead */

.ja-masthead {
	background: var(--ja-bg-sunken);
	border-bottom: 1px solid var(--ja-rule);
}

.ja-masthead-inner {
	display: flex;
	align-items: center;
	gap: var(--ja-gap);
	min-height: calc(var(--ja-root) * 4);
	flex-wrap: wrap;
}

.ja-logo {
	font-family: var(--ja-serif);
	font-size: calc(var(--ja-root) * 1.5);
	font-weight: 700;
	color: var(--ja-ink-strong);
	text-decoration: none;
	line-height: var(--ja-leading-tight);
}
.ja-logo-2 { color: var(--ja-accent); }

.ja-nav-list {
	display: flex;
	gap: calc(var(--ja-root) * 1);
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: var(--ja-size-meta);
	flex-wrap: wrap;
}
/*
 * The li below already reserves a 44px row, but a bare inline `a` only takes
 * up its own text height inside that row (the audit measured it at 24px):
 * flex plus the same min-height makes the link itself fill the row, so the
 * whole row is tappable, not just the letters in it.
 */
.ja-nav-list a {
	display: flex;
	align-items: center;
	min-height: calc(var(--ja-root) * 2.75);
	color: var(--ja-ink);
	text-decoration: none;
}
.ja-nav-list a:hover { color: var(--ja-accent); }
.ja-nav-list li { min-height: calc(var(--ja-root) * 2.75); display: flex; align-items: center; }

/*
 * Hidden by default: the collapsible small screen nav only exists below the
 * breakpoint (see the max-width: 720px block near the bottom of this file).
 * Desktop layout above it is completely untouched.
 */
.ja-nav-toggle {
	display: none;
	min-width: calc(var(--ja-root) * 2.75);
	min-height: calc(var(--ja-root) * 2.75);
	margin-left: auto;
	padding: 0;
	background: transparent;
	border: 1px solid var(--ja-rule-strong);
	border-radius: var(--ja-radius);
	color: var(--ja-ink);
	cursor: pointer;
}
.ja-nav-toggle[aria-expanded="true"] { border-color: var(--ja-accent); color: var(--ja-accent); }

.ja-nav-toggle-icon {
	position: relative;
	display: block;
	width: calc(var(--ja-root) * 1.25);
	height: calc(var(--ja-root) * 0.125);
	margin: 0 auto;
	background: currentColor;
}
.ja-nav-toggle-icon::before,
.ja-nav-toggle-icon::after {
	content: '';
	position: absolute;
	left: 0;
	width: 100%;
	height: 100%;
	background: currentColor;
}
.ja-nav-toggle-icon::before { top: calc(var(--ja-root) * -0.375); }
.ja-nav-toggle-icon::after { top: calc(var(--ja-root) * 0.375); }

.ja-search { margin-left: auto; display: flex; gap: calc(var(--ja-root) * 0.375); }
.ja-search input {
	min-height: calc(var(--ja-root) * 2.75);
	padding: 0 calc(var(--ja-root) * 0.625);
	background: var(--ja-bg);
	color: var(--ja-ink);
	border: 1px solid var(--ja-rule-strong);
	border-radius: var(--ja-radius);
	font-size: var(--ja-root); /* 16px: below this iOS zooms the page */
	font-family: inherit;
}
.ja-search button {
	min-height: calc(var(--ja-root) * 2.75);
	padding: 0 calc(var(--ja-root) * 0.875);
	background: var(--ja-accent);
	color: var(--ja-accent-ink);
	border: 0;
	border-radius: var(--ja-radius);
	font-size: var(--ja-size-meta);
	font-weight: 700;
	cursor: pointer;
}

/*
 * Sub-menus. The theme carried no rule for these at all: whatever menu an
 * editor assigns to `primary` or `footer` may have children, and until this
 * they rendered as an unstyled `<ul>` floating to the right of the masthead.
 *
 * Hidden by opacity/pointer-events, not `display` or `visibility`: both of
 * those pull child links out of the tab order, which breaks the
 * `:focus-within` reveal below (a link that cannot be focused can never
 * satisfy `:focus-within`). Revealed on `:hover` OR `:focus-within`, never
 * hover alone, so tabbing to a child with a keyboard opens the same dropdown
 * a mouse hover does.
 */
.ja-nav-list li { position: relative; }

.ja-nav-list .sub-menu {
	position: absolute;
	top: 100%;
	left: 0;
	z-index: 20;
	margin: 0;
	padding: calc(var(--ja-root) * 0.25) 0;
	min-width: calc(var(--ja-root) * 11);
	/* Bounded to the viewport, not just its own content: a long child label
	   must not be what pushes this past the right edge. */
	max-width: calc(100vw - var(--ja-gap) * 2);
	list-style: none;
	background: var(--ja-bg-sunken);
	border: 1px solid var(--ja-rule);
	border-radius: var(--ja-radius);
	opacity: 0;
	pointer-events: none;
	transform: translateY(calc(var(--ja-root) * -0.25));
	transition: opacity 0.15s ease, transform 0.15s ease;
}

.ja-nav-list li:hover > .sub-menu,
.ja-nav-list li:focus-within > .sub-menu {
	opacity: 1;
	pointer-events: auto;
	transform: translateY(0);
}

.ja-nav-list .sub-menu li { min-height: calc(var(--ja-root) * 2.75); width: 100%; }
.ja-nav-list .sub-menu a {
	display: flex;
	align-items: center;
	width: 100%;
	min-height: calc(var(--ja-root) * 2.75);
	padding: 0 calc(var(--ja-root) * 0.875);
	white-space: nowrap;
}
.ja-nav-list .sub-menu a:hover { background: var(--ja-bg-2); }

/*
 * Safety net for the max-width clamp above: it bounds the dropdown's own
 * box, not where that box sits relative to the parent link, so a dropdown
 * under a nav item near the right edge could still render partly past the
 * viewport. The masthead spans the full viewport width with no horizontal
 * margin of its own, so clipping overflow here is equivalent to clipping at
 * the viewport edge, and guarantees no menu, at any width, ever opens a
 * horizontal scrollbar on the page.
 */
.ja-masthead { overflow-x: clip; }

/* footer */

.ja-footer {
	margin-top: var(--ja-gap-lg);
	padding: var(--ja-gap-lg) 0;
	background: var(--ja-bg-sunken);
	border-top: 1px solid var(--ja-rule);
	font-size: var(--ja-size-meta);
	color: var(--ja-muted);
}
.ja-footer-line { max-width: var(--ja-measure); }

/*
 * --ja-muted clears 4.5:1 but is only verified at 14px and up (see
 * tokens.css). This line sits at the 12px floor, so it opts back into
 * --ja-ink rather than inheriting --ja-footer's muted colour.
 */
.ja-footer-meta { font-size: var(--ja-size-label); color: var(--ja-ink); }

@media (max-width: 720px) {
	.ja-search { margin-left: 0; width: 100%; order: 3; flex-basis: 100%; }
	.ja-nav-list { gap: calc(var(--ja-root) * 0.75); }
	/* `nowrap` labels plus the 100vw clamp above are enough to stop a
	   scrollbar, but on a narrow phone width the two together can still
	   clip a long label mid-word. Wrapping the text instead reads better
	   than a truncated word does. */
	.ja-nav-list .sub-menu { min-width: calc(var(--ja-root) * 9); }
	.ja-nav-list .sub-menu a { white-space: normal; }

	/*
	 * Small screen nav (I1/I2 in the audit). The six item bar used to wrap
	 * onto three lines here instead of collapsing, pushing roughly 400px of
	 * chrome above any real content.
	 *
	 * This block is the no-JS-required baseline: a plain full width vertical
	 * stack, one 44px row per item, not a wrap. It is reachable with zero
	 * script. The rule right after it, gated on html.ja-js, is the only part
	 * that actually hides the list, and that class is only ever added by
	 * nav-toggle.js once it has confirmed it can reopen what it hides. If
	 * that script never runs, this baseline is what stays on screen: no
	 * toggle button doing nothing, no menu stuck closed forever.
	 */
	.ja-nav-toggle { display: inline-flex; align-items: center; justify-content: center; order: 1; }
	.ja-nav { flex-basis: 100%; order: 2; }
	.ja-nav-list { flex-direction: column; gap: 0; }
	.ja-nav-list li { width: 100%; border-top: 1px solid var(--ja-rule); }
	.ja-nav-list a { width: 100%; }

	html.ja-js .ja-nav { display: none; }
	html.ja-js .ja-nav.is-open { display: block; }
}

/* ------------------------------------------------------------- article ---- */

.ja-article-wrap {
	display: flex;
	gap: var(--ja-gap-lg);
	align-items: flex-start;
	padding-top: var(--ja-gap-lg);
}

.ja-article { flex: 1; min-width: 0; max-width: var(--ja-measure); }

/*
 * --ja-muted is only verified at 14px and up (see tokens.css). The crumb sits
 * at the 12px label floor, so it opts into --ja-ink rather than --ja-muted,
 * the same fix .ja-footer-meta already needed for the same reason.
 */
.ja-crumb { margin: 0 0 calc(var(--ja-root) * 0.5); font-size: var(--ja-size-label); color: var(--ja-ink); text-transform: uppercase; letter-spacing: 0.08em; }
.ja-crumb a { position: relative; color: var(--ja-ink); text-decoration: none; }
/*
 * The audit measured this link at 17px tall: a breadcrumb has to print at
 * the 12px label size, so growing the visible text or padding is not an
 * option without it stopping looking like a breadcrumb. An invisible
 * pseudo-element floor instead extends the actual tappable box to 44px
 * around the text, without touching what is on screen or what any sibling
 * link (the_category(' / ') can print more than one) reserves for itself.
 */
.ja-crumb a::before {
	content: '';
	position: absolute;
	inset: calc(var(--ja-root) * -1) calc(var(--ja-root) * -0.25);
}

.ja-h1 {
	margin: 0 0 calc(var(--ja-root) * 0.5);
	font-family: var(--ja-serif);
	font-size: var(--ja-size-h1);
	font-weight: 700;
	line-height: var(--ja-leading-tight);
	color: var(--ja-ink-strong);
}

/*
 * 20px serif roman, not the old 17px italic dimmed tint: this is the small
 * fraction of posts (23 of 5,728) whose stored excerpt survives Dek's checks
 * and is worth reading, so it earns primary content treatment, not a
 * metadata one. Italic reads fine as a single line label but strains across
 * the two or three line paragraph a real dek runs to, so it drops to roman
 * weight; --ja-ink, not a dimmed mix, is the same full body colour used
 * below it, already verified at 11.11:1 (see tokens.css).
 */
.ja-dek {
	margin: 0 0 var(--ja-gap);
	font-family: var(--ja-serif);
	font-size: calc(var(--ja-root) * 1.25);
	font-weight: 400;
	line-height: var(--ja-leading);
	color: var(--ja-ink);
}

.ja-byline {
	display: flex;
	align-items: center;
	gap: calc(var(--ja-root) * 0.75);
	margin: 0 0 var(--ja-gap);
	padding: calc(var(--ja-root) * 0.5) 0;
	border-top: 1px solid var(--ja-rule);
	border-bottom: 1px solid var(--ja-rule);
	font-size: var(--ja-size-meta);
	color: var(--ja-muted);
	flex-wrap: wrap;
}
.ja-byline strong { color: var(--ja-ink); }

.ja-grade {
	margin-left: auto;
	font-family: var(--ja-serif);
	font-size: calc(var(--ja-root) * 1.9375);
	font-weight: 700;
	line-height: 1;
	color: var(--ja-accent);
}

.ja-hero { margin: 0 0 var(--ja-gap); }
.ja-hero img { border-radius: var(--ja-radius); }

.ja-prose > * + * { margin-top: 1.15em; }
.ja-prose h2 { font-family: var(--ja-serif); font-size: var(--ja-size-h2); line-height: var(--ja-leading-tight); color: var(--ja-ink-strong); margin-top: 2em; }
.ja-prose h3 { font-size: calc(var(--ja-root) * 1.25); color: var(--ja-ink-strong); margin-top: 1.8em; }
.ja-prose blockquote {
	margin: 1.4em 0; padding: 0.2em 0 0.2em 1em;
	border-left: 3px solid var(--ja-accent);
	font-family: var(--ja-serif);
	font-size: calc(var(--ja-root) * 1.25);
	color: var(--ja-ink-strong);
}
/* max()'s floor is the --ja-size-label token, not a bare 12px: nothing in this
   file renders below that floor, and this keeps the guarantee token-driven. */
.ja-prose code, .ja-prose pre { font-family: var(--ja-mono); font-size: max(var(--ja-size-label), 0.86em); }
.ja-prose pre {
	padding: calc(var(--ja-root) * 0.75);
	background: var(--ja-bg-sunken);
	border: 1px solid var(--ja-rule);
	border-radius: var(--ja-radius);
	overflow-x: auto;
}
.ja-prose img { border-radius: var(--ja-radius); }
.ja-prose table { width: 100%; border-collapse: collapse; font-size: var(--ja-size-meta); display: block; overflow-x: auto; }
.ja-prose td, .ja-prose th { border-bottom: 1px solid var(--ja-rule); padding: calc(var(--ja-root) * 0.4); text-align: left; }

/*
 * Neutralise legacy inline typography left behind by the Joomla/EE migration.
 * Measured across published posts and walkthroughs: 2,414 carry a dark inline
 * `color` (often the literal #333333, which computes to roughly 1.3:1 against
 * this theme's #242118 background) and 1,892 carry an inline `pt` font size
 * (10pt Helvetica is 13.3px, and drops under the 12px floor once nested
 * inside another sized span). `post_content` is the archive and this
 * project's spec forbids rewriting it, so the fix has to run at render time.
 *
 * An inline style attribute always outranks a stylesheet selector, no matter
 * how specific: `!important` is the only way a rule here can still win.
 * Scoped to text level elements only, never `img`, whose inline `style`
 * carries real sizing (`max-width`) that has to survive. `font-weight` and
 * `font-style` are deliberately absent from the list below: semantic
 * <b>/<strong> and <i>/<em> must keep rendering bold and italic, so only the
 * four properties that actually carry migration debris (colour, size,
 * family, background) are reset, and `inherit` hands them back to whatever
 * this theme already set on the wrapping element.
 */
.ja-prose span, .ja-prose p, .ja-prose div, .ja-prose td, .ja-prose th,
.ja-prose li, .ja-prose b, .ja-prose i, .ja-prose strong, .ja-prose em {
	color: inherit !important;
	background: inherit !important;
	font-family: inherit !important;
	font-size: inherit !important;
}

.ja-tags { margin-top: var(--ja-gap-lg); font-size: var(--ja-size-label); }
/* same 12px floor as .ja-crumb: --ja-ink, not --ja-muted. */
.ja-tags a { color: var(--ja-ink); }

/* ---------------------------------------------------------------- rail ---- */

.ja-rail { width: calc(var(--ja-root) * 13); flex: none; position: sticky; top: var(--ja-gap); }

.ja-gamecard {
	/* Cover plus the padding either side, so the card is exactly as wide as the
	   poster it frames however wide its container is. See --ja-cover-max. */
	max-width: calc(var(--ja-cover-max) + var(--ja-root) * 1.25);
	background: var(--ja-bg-2);
	border: 1px solid var(--ja-rule);
	border-radius: var(--ja-radius);
	padding: calc(var(--ja-root) * 0.625);
	font-size: var(--ja-size-meta);
}
/* 3px per the brief, not --ja-radius (5px): a cover thumbnail sits inside an
   already-rounded card, and radius is neither colour nor font size, so the
   tokens-only constraint does not reach it. Revert if a reviewer prefers 5px. */
/* The cover keeps its own shape: the catalogue holds both 3:4 box art (1,283 of
   1,403 covers) and wide banners (109), so there is no one ratio to force them
   into and nothing on this card to line a fixed box up against. Bounded on both
   axes instead, the height cap being a 3:4 poster at the maximum width, so only
   a taller-than-poster outlier is bounded at all and it letterboxes on the
   card's own surface rather than being cropped. */
.ja-gamecard-cover {
	display: block;
	width: 100%;
	max-width: var(--ja-cover-max);
	height: auto;
	max-height: calc(var(--ja-cover-max) * 4 / 3);
	object-fit: contain;
	border-radius: 3px;
	margin-bottom: calc(var(--ja-root) * 0.5);
}
.ja-gamecard-title { margin: 0; font-family: var(--ja-serif); font-size: calc(var(--ja-root) * 1.0625); line-height: var(--ja-leading-tight); }
.ja-gamecard-title a { color: var(--ja-ink-strong); text-decoration: none; }
/* meta line sits at the 12px label floor: --ja-ink, not --ja-muted, same as
   .ja-crumb and .ja-tags a above. */
.ja-gamecard-meta { margin: calc(var(--ja-root) * 0.125) 0 calc(var(--ja-root) * 0.375); color: var(--ja-ink); font-size: var(--ja-size-label); }
.ja-gamecard-guides { margin: 0 0 calc(var(--ja-root) * 0.5); }

/*
 * A link styled as a control, not a divider: judged against the 3:1 non-text
 * floor like .ja-search button, which this mirrors. Its accent fill against
 * --ja-bg already clears that (7.28:1 per tokens.css), so it needs no border
 * of its own, --ja-rule-strong or otherwise.
 */
.ja-btn {
	display: block;
	min-height: calc(var(--ja-root) * 2.75);
	line-height: calc(var(--ja-root) * 2.75);
	text-align: center;
	background: var(--ja-accent);
	color: var(--ja-accent-ink);
	border-radius: var(--ja-radius);
	font-size: var(--ja-size-meta);
	font-weight: 700;
	text-decoration: none;
}

@media (max-width: 900px) {
	.ja-article-wrap { display: block; }
	.ja-rail { width: auto; position: static; margin-top: var(--ja-gap-lg); }
}

/* ----------------------------------------------------------- list views ---- */

.ja-listhead { padding: var(--ja-gap-lg) 0 var(--ja-gap); }
.ja-listdesc { color: var(--ja-muted); font-size: var(--ja-size-meta); max-width: var(--ja-measure); }

.ja-cards {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(calc(var(--ja-root) * 17), 1fr));
	gap: var(--ja-gap);
}

.ja-card { background: var(--ja-bg-2); border: 1px solid var(--ja-rule); border-radius: var(--ja-radius); overflow: hidden; }
.ja-card-link { display: block; color: inherit; text-decoration: none; height: 100%; }
/* --ja-bg-sunken, not a literal darkening of --ja-bg: this file carries no
   literal colour. It reads as an empty image slot behind the <img> until the
   thumbnail paints, the same "recessed" surface tokens.css already uses for
   the chrome bands and the code shelf, applied here for a third reason. */
.ja-card-thumb { aspect-ratio: 16 / 9; background: var(--ja-bg-sunken); }
.ja-card-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ja-card-body { padding: calc(var(--ja-root) * 0.75); }
/*
 * Kicker and meta both sit at --ja-size-label (12px), the floor where
 * --ja-muted is not verified (see tokens.css). Same fix as .ja-crumb,
 * .ja-tags a and .ja-gamecard-meta: --ja-ink instead, which the tokens.css
 * docblock documents at 10.25:1 on --ja-bg-2, the surface a card sits on.
 */
.ja-card-kicker {
	display: flex; align-items: baseline; gap: calc(var(--ja-root) * 0.5);
	margin: 0 0 calc(var(--ja-root) * 0.25);
	font-size: var(--ja-size-label); text-transform: uppercase; letter-spacing: 0.08em; color: var(--ja-ink);
}
.ja-card-grade { margin-left: auto; font-family: var(--ja-serif); font-size: var(--ja-size-meta); color: var(--ja-accent); letter-spacing: 0; }
.ja-card-title { margin: 0; font-family: var(--ja-serif); font-size: calc(var(--ja-root) * 1.125); line-height: var(--ja-leading-tight); color: var(--ja-ink-strong); }
.ja-card-meta { margin: calc(var(--ja-root) * 0.25) 0 0; font-size: var(--ja-size-label); color: var(--ja-ink); }
/*
 * The byline and part number that tell two guides for one game apart. Set
 * before the date and separated from it, because on a walkthrough card it is
 * the part that varies: every imported guide carries the same import date.
 */
.ja-card-credit { display: block; color: var(--ja-ink-strong); }
.ja-card:hover { border-color: var(--ja-accent); }

/*
 * No thumbnail: not a broken card, a different one. No placeholder image,
 * gradient or monogram stands in for the missing cover (deliberate call, see
 * task-4-report.md); instead the accent rule across the top edge and the
 * larger, more generously padded title carry the card on type alone.
 * align-self: start keeps the card's own height honest instead of stretching
 * it to match a taller image card sharing its grid row with empty space.
 */
.ja-card--text {
	border-top: 2px solid var(--ja-accent);
	align-self: start;
}
.ja-card--text .ja-card-body { padding: var(--ja-gap); }
.ja-card--text .ja-card-title { font-size: var(--ja-size-h2); }

.ja-empty { color: var(--ja-muted); }

.ja-pagination { display: flex; flex-wrap: wrap; gap: calc(var(--ja-root) * 0.375); margin: var(--ja-gap-lg) 0; font-size: var(--ja-size-meta); }
/*
 * --ja-rule-strong, not --ja-rule: a pagination link is a control, not a
 * decorative hairline, and needs the 3:1 non-text floor --ja-rule-strong
 * clears (see tokens.css). Mirrors the .ja-search input fix from Task 3.
 */
.ja-pagination a, .ja-pagination .current {
	display: inline-flex; align-items: center; justify-content: center;
	min-width: calc(var(--ja-root) * 2.75); min-height: calc(var(--ja-root) * 2.75);
	padding: 0 calc(var(--ja-root) * 0.5);
	border: 1px solid var(--ja-rule-strong); border-radius: var(--ja-radius); text-decoration: none;
}
.ja-pagination .current { background: var(--ja-accent); color: var(--ja-accent-ink); border-color: var(--ja-accent); font-weight: 700; }

/* ------------------------------------------------------------ homepage ---- */

.ja-home { padding-top: var(--ja-gap-lg); }
.ja-home-grid { display: grid; grid-template-columns: 1.55fr 1fr; gap: var(--ja-gap-lg); }

.ja-lead-link { color: inherit; text-decoration: none; display: block; }
.ja-lead-thumb { border-radius: var(--ja-radius); overflow: hidden; margin-bottom: calc(var(--ja-root) * 0.75); }
.ja-lead-thumb img { width: 100%; display: block; }
.ja-lead-title { margin: 0 0 calc(var(--ja-root) * 0.375); font-family: var(--ja-serif); font-size: var(--ja-size-h2); line-height: var(--ja-leading-tight); color: var(--ja-ink-strong); }
.ja-lead-dek { margin: 0; color: var(--ja-muted); font-size: var(--ja-size-meta); }

.ja-panel { background: var(--ja-bg-2); border: 1px solid var(--ja-rule); border-radius: var(--ja-radius); padding: var(--ja-gap); }
.ja-panel + .ja-panel { margin-top: var(--ja-gap); }
.ja-panel-title { margin: 0 0 calc(var(--ja-root) * 0.625); font-family: var(--ja-serif); font-size: calc(var(--ja-root) * 1.25); color: var(--ja-ink-strong); }
.ja-panel-copy, .ja-panel-more { font-size: var(--ja-size-meta); color: var(--ja-muted); }
.ja-panel-search { display: flex; gap: calc(var(--ja-root) * 0.375); margin-bottom: calc(var(--ja-root) * 0.75); flex-wrap: wrap; }
/*
 * --ja-rule-strong, not --ja-rule: a search input is a control, not a
 * decorative hairline, and needs the 3:1 non-text floor --ja-rule-strong
 * clears (see tokens.css). Mirrors the .ja-search input fix from Task 3.
 */
.ja-panel-search input[type="search"] {
	flex: 1; min-width: calc(var(--ja-root) * 9); min-height: calc(var(--ja-root) * 2.75);
	padding: 0 calc(var(--ja-root) * 0.625); font-size: var(--ja-root);
	background: var(--ja-bg); color: var(--ja-ink); border: 1px solid var(--ja-rule-strong); border-radius: var(--ja-radius);
	font-family: inherit;
}
.ja-panel-search button {
	min-height: calc(var(--ja-root) * 2.75); padding: 0 calc(var(--ja-root) * 0.875);
	background: var(--ja-accent); color: var(--ja-accent-ink); border: 0; border-radius: var(--ja-radius);
	font-size: var(--ja-size-meta); font-weight: 700; cursor: pointer;
}
.ja-panel-list { margin: 0 0 calc(var(--ja-root) * 0.5); padding: 0; list-style: none; font-size: var(--ja-size-meta); }
.ja-panel-list li + li { margin-top: calc(var(--ja-root) * 0.375); padding-top: calc(var(--ja-root) * 0.375); border-top: 1px solid var(--ja-rule); }

.ja-stats { display: flex; margin: 0 0 calc(var(--ja-root) * 0.625); padding: calc(var(--ja-root) * 0.625) 0 0; border-top: 1px solid var(--ja-rule); }
.ja-stats > div { flex: 1; text-align: center; }
/* --ja-ink, not --ja-muted: same 12px floor fix as .ja-card-kicker above. */
.ja-stats dt { font-size: var(--ja-size-label); text-transform: uppercase; letter-spacing: 0.08em; color: var(--ja-ink); }
.ja-stats dd { margin: 0; font-family: var(--ja-serif); font-size: calc(var(--ja-root) * 1.375); color: var(--ja-accent); }

/*
 * The three full width sections below the lead/side grid: Recent, Reviews,
 * Recently added guides. Each reuses .ja-cards, the same grid list.php
 * views already use, so a card looks and behaves identically wherever it
 * appears on the site.
 */
.ja-home-section { margin-top: var(--ja-gap-lg); }
.ja-home-section-head {
	display: flex; align-items: baseline; justify-content: space-between; gap: var(--ja-gap);
	margin-bottom: var(--ja-gap);
	padding-bottom: calc(var(--ja-root) * 0.625);
	border-bottom: 1px solid var(--ja-rule);
}
.ja-home-section-title { margin: 0; font-family: var(--ja-serif); font-size: var(--ja-size-h2); line-height: var(--ja-leading-tight); color: var(--ja-ink-strong); }
.ja-home-section-more { font-size: var(--ja-size-meta); color: var(--ja-accent); text-decoration: none; white-space: nowrap; }
.ja-home-section-more:hover { text-decoration: underline; }

@media (max-width: 900px) {
	.ja-home-grid { grid-template-columns: 1fr; }
}

/* ------------------------------------------------------- recirculation ---- */

/*
 * What to read next, printed below the article/rail row (see single.php,
 * Nivoli\JA\Recirculation). Reuses .ja-cards/.ja-card for the entries and
 * mirrors .ja-home-section-head's layout for the block heading, so a
 * suggestion looks and behaves like every other card grid on the site
 * rather than introducing a fourth visual language for "a list of posts".
 */
.ja-recirc { margin-top: var(--ja-gap-lg); padding-top: var(--ja-gap-lg); border-top: 1px solid var(--ja-rule); }
.ja-recirc-head {
	display: flex; align-items: baseline; justify-content: space-between; gap: var(--ja-gap);
	margin-bottom: var(--ja-gap);
	padding-bottom: calc(var(--ja-root) * 0.625);
	border-bottom: 1px solid var(--ja-rule);
}
.ja-recirc-title { margin: 0; font-family: var(--ja-serif); font-size: var(--ja-size-h2); line-height: var(--ja-leading-tight); color: var(--ja-ink-strong); }
.ja-recirc-more { font-size: var(--ja-size-meta); color: var(--ja-accent); text-decoration: none; white-space: nowrap; }
.ja-recirc-more:hover { text-decoration: underline; }
/*
 * The guides list sits below the sibling-articles grid inside the SAME game
 * block, so it gets a subtitle one size down from .ja-recirc-title, no
 * border or "more" link of its own: it belongs to the block above it, not a
 * second block.
 */
.ja-recirc-subtitle { margin: var(--ja-gap) 0 calc(var(--ja-root) * 0.625); font-family: var(--ja-serif); font-size: calc(var(--ja-root) * 1.25); color: var(--ja-ink-strong); }

.ja-recirc-fallback { display: flex; flex-wrap: wrap; gap: var(--ja-gap); margin: 0; }
/* flex-basis derives from --ja-root like every other size in this file, not
   a bare px value: two links side by side on desktop, stacked full width
   the moment they no longer fit. */
.ja-recirc-btn { flex: 1 1 calc(var(--ja-root) * 13); }

/* ------------------------------------------- article enhancements ---- */

/* Pros and cons cards and the click to load YouTube facade. Both were styled in
   the Customizer record and driven by a script injected through the retired
   theme's footer copy option. See assets/js/enhance.js. */

.ja-proscons {
	display: flex; flex-wrap: wrap; gap: calc(var(--ja-root) * 1.125);
	margin: calc(var(--ja-root) * 1.6) 0;
	font-family: var(--ja-sans);
}
.ja-proscons .ja-col {
	flex: 1 1 calc(var(--ja-root) * 16.25);
	background: var(--ja-bg-2); border: 1px solid var(--ja-rule); border-radius: calc(var(--ja-root) * 0.5);
	padding: calc(var(--ja-root) * 0.875) calc(var(--ja-root) * 1.125);
}
/*
 * 14px (--ja-size-meta), not the original 13px: there is no token for 13,
 * and the header colour below (--ja-good) is only checked at 4.5:1 against
 * --ja-bg-2, the tier the binding rules only clear text at 14px and up.
 */
.ja-proscons h4 { margin: 0 0 calc(var(--ja-root) * 0.5); font-size: var(--ja-size-meta); letter-spacing: 0.06em; text-transform: uppercase; font-weight: 600; }
/* --ja-good on --ja-bg-2 measures ~4.98:1, clears the 4.5:1 floor this heading's
   14px size is entitled to (see the note above). */
.ja-proscons .ja-pros h4 { color: var(--ja-good); }
/*
 * --ja-ink-strong, not a "bad" colour: tokens.css has no red/danger token,
 * and inventing a literal hex here would recreate the very drift Task 9
 * retires the Customizer record to end. "The Bad" and the leading "–" glyph
 * already carry the meaning colour used to on the old site; --ja-ink-strong
 * (13.18:1 on --ja-bg-2, per tokens.css) keeps the heading legible without one.
 */
.ja-proscons .ja-cons h4 { color: var(--ja-ink-strong); }
.ja-proscons ul { list-style: none; margin: 0; padding: 0; }
/*
 * font-size: --ja-size-guide (18px), the closer of the two body tokens to
 * the original 17px. line-height: --ja-leading (1.7), the closer of the two
 * leading tokens to the original 1.5; tokens.css defines only 1.7 and 1.2.
 * color: --ja-ink is an exact match for the original #ddd6c6.
 */
.ja-proscons li {
	position: relative; padding-left: calc(var(--ja-root) * 1.375); margin: calc(var(--ja-root) * 0.375) 0;
	color: var(--ja-ink); line-height: var(--ja-leading); font-size: var(--ja-size-guide);
}
.ja-proscons .ja-pros li::before { content: '+'; position: absolute; left: 0; top: -1px; color: var(--ja-good); font-weight: 700; }
.ja-proscons .ja-cons li::before { content: '–'; position: absolute; left: 0; top: -1px; color: var(--ja-ink-strong); font-weight: 700; }

/*
 * The facade is a <button> now (see enhance.js), not a div, so the usual
 * UA button chrome is reset here: border, background shorthand, padding,
 * font and text-align all need an explicit value they never did as a div.
 * --ja-bg-sunken for the poster-frame well: the same "recessed surface
 * behind an image before it paints" role tokens.css already documents for
 * .ja-card-thumb, extended to a video poster instead of a cover image.
 */
.ja-lite-yt {
	display: block; position: relative; width: 100%; aspect-ratio: 16 / 9;
	background: var(--ja-bg-sunken) center/cover no-repeat;
	border: 0; border-radius: calc(var(--ja-root) * 0.5); padding: 0; margin: calc(var(--ja-root) * 1.4) 0;
	font: inherit; text-align: left; appearance: none; cursor: pointer; overflow: hidden;
}
/*
 * The original dimmed this layer with rgba(0,0,0,.12), a literal colour.
 * An opaque --ja-bg-sunken fill at 12% opacity composites to the same
 * result without one, and clearing to 0 opacity on hover reproduces the
 * original's rgba(0,0,0,0).
 */
.ja-lite-yt::after { content: ''; position: absolute; inset: 0; background: var(--ja-bg-sunken); opacity: 0.12; transition: opacity 0.2s; }
.ja-lite-yt:hover::after { opacity: 0; }
/*
 * Opaque, not the original's rgba(20,20,20,.8): a translucent pill's
 * contrast against the poster photo behind it depends on that photo, so it
 * cannot be guaranteed. Solid --ja-bg-sunken against the --ja-ink-strong
 * triangle below measures ~15.6:1, clearing the 3:1 non-text floor
 * regardless of what image sits under it.
 */
.ja-lite-yt-play {
	position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
	width: calc(var(--ja-root) * 4.25); height: calc(var(--ja-root) * 3);
	border: 0; border-radius: calc(var(--ja-root) * 0.875); background: var(--ja-bg-sunken);
	transition: background 0.2s;
}
.ja-lite-yt-play::before {
	content: ''; position: absolute; left: 50%; top: 50%; transform: translate(-40%, -50%);
	border-style: solid; border-width: 10px 0 10px 18px;
	border-color: transparent transparent transparent var(--ja-ink-strong);
}
/*
 * The original swapped this pill to #f00, YouTube's brand red, on hover:
 * a literal colour with no token, and no equivalent one belongs in this
 * system. --ja-accent is this theme's own hover/active colour instead, and
 * --ja-accent-ink is the token already established (search button, .ja-btn)
 * for text and icons that sit on an --ja-accent fill, so the triangle
 * repaints to it here too. Computed pairing, ~7.4:1, clears the 3:1 non-text
 * floor; --ja-ink-strong would not (~2:1 against --ja-accent), which is why
 * it only appears in the base state above.
 */
.ja-lite-yt:hover .ja-lite-yt-play { background: var(--ja-accent); }
.ja-lite-yt:hover .ja-lite-yt-play::before { border-color: transparent transparent transparent var(--ja-accent-ink); }

/* ------------------------------------------- migrated from Customizer ---- */

/* What survived the retired theme. Everything that targeted .article-post-content,
   .article_body, #page-header, #top-navigation, #header-navigation, .sidebar,
   #page-content, #page-footer, .entry-meta or body.night-mode went away with the
   theme it was patching, along with the forced dark background and the Inter
   @import: this theme is dark by default, self-hosts Inter, and needs no
   !important to prove either. The pros and cons and lite YouTube rules moved
   in Task 6, with their script. The table border reset is Task 3's
   .ja-prose table / td / th, already in this file. */

/* Old reviews embed a white logo GIF inside a layout table. It reads as a hole
   punched in the page on a dark background. */
.ja-prose table img[alt="Logo" i] { display: none; }

/* Browse by Topic. Ordinary page content, so it outlives any theme. */
.ja-topics { max-width: var(--ja-measure); }
.ja-topics h2 { font-family: var(--ja-serif); font-size: var(--ja-size-h2); color: var(--ja-ink-strong); }
.ja-topics-intro, .ja-topics-foot { color: var(--ja-muted); font-size: var(--ja-size-meta); }
.ja-topics-intro a, .ja-topics-foot a { color: var(--ja-accent); }

.ja-tagcloud { display: flex; flex-wrap: wrap; gap: calc(var(--ja-root) * 0.5); }
.ja-tagcloud a {
	display: inline-flex; align-items: center;
	min-height: calc(var(--ja-root) * 2.75);
	padding: 0 calc(var(--ja-root) * 0.625);
	border: 1px solid var(--ja-rule); border-radius: 999px;
	font-size: var(--ja-size-meta); text-decoration: none;
}
.ja-tagcloud a:hover { border-color: var(--ja-accent); }

/*
 * Popularity tiers (t1..t5): the page markup itself carries these classes per
 * tag, one per link, sized 13/15/17/19/23px in the original. Kept as calc()
 * steps off --ja-root so the cloud still reads as a cloud, biggest tags
 * heaviest. t4 reuses --ja-size-body (already 19px) rather than a new step.
 * t4/t5 also pick up a background: --ja-bg-2, the theme's established raised
 * surface (see .ja-gamecard), so the busiest tags still stand off the page
 * the way the original's bespoke shades did.
 */
.ja-tagcloud .t1 { font-size: calc(var(--ja-root) * 0.8125); opacity: 0.92; }
.ja-tagcloud .t2 { font-size: calc(var(--ja-root) * 0.9375); }
.ja-tagcloud .t3 { font-size: calc(var(--ja-root) * 1.0625); }
.ja-tagcloud .t4 { font-size: var(--ja-size-body); font-weight: 600; background: var(--ja-bg-2); }
.ja-tagcloud .t5 {
	font-size: calc(var(--ja-root) * 1.4375); font-weight: 700;
	color: var(--ja-ink-strong); background: var(--ja-bg-2); border-color: var(--ja-accent);
}

/* By platform: same pill, no popularity tiers, a quieter label colour. */
.ja-platforms a { font-size: calc(var(--ja-root) * 0.9375); color: var(--ja-muted); }

/*
 * Count badge. The original set font-size: .74em against the *link's* size,
 * which on a t1 tag (13px) resolves under the 12px floor. max() against the
 * label token is the same fix already used for .ja-prose code/pre. Because
 * that floor can bind, this sits at the 12px floor like .ja-crumb and
 * .ja-gamecard-meta: --ja-ink, not --ja-muted (see tokens.css).
 */
.ja-tagcloud a span {
	margin-left: calc(var(--ja-root) * 0.3125);
	padding: 0.12em calc(var(--ja-root) * 0.34375);
	border-radius: 999px;
	background: var(--ja-bg-sunken);
	color: var(--ja-ink);
	font-size: max(var(--ja-size-label), 0.74em);
	font-weight: 600;
}
.ja-lite-yt iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
