/*
 * Nexa PVP — the panel's design system.
 *
 * Everything below the tokens is derived from them, so the whole site re-themes
 * from this one block. Not tidiness for its own sake: this product is sold to
 * server owners who will want their own colour, and a palette spread across three
 * thousand lines is a palette nobody can change.
 *
 * -- The idea --------------------------------------------------------------
 *
 * Knight Online is a permanent two-sided war. Every other panel for this game
 * presents its ladders as one neutral list, which throws away the only thing the
 * data is about: every point on it was taken from somebody on the other side. So
 * the organising device here is the **war line** — a two-colour bar carrying the
 * live share of national points between Karus and El Morad. It is the bottom edge
 * of the masthead on every page, and the front page opens with it labelled. It is
 * drawn from `RankingRepository::nations()`, so it moves when the server does.
 *
 * That is also why the chrome is **gold and takes no side**. The accent is the
 * game's own interface colour — its item frames, its buttons, its panels are gold
 * and bronze on near-black — so it reads as "the game" rather than as a faction.
 * Red and blue appear only where a nation is the actual data.
 *
 * -- The type --------------------------------------------------------------
 *
 * Three faces, three jobs, and the split is the point:
 *
 *   Cinzel      Roman inscriptional caps. Titles, the wordmark, nation names,
 *               and nothing else. The game's own logo is Trajan-like and its
 *               world is heraldic; carved capitals belong to it in a way a
 *               grotesque does not. At four places per page it reads as
 *               engraving; anywhere else it would read as a fantasy poster,
 *               which is why the rule is strict.
 *   Barlow      Slightly condensed, low contrast, full Turkish coverage. Carries
 *               every sentence. Narrower than the usual choice, which buys a
 *               column of player names two more characters.
 *   JetBrains   Every number. A ladder is a column of figures and figures have
 *   Mono        to line up, so `tabular-nums` is set on the data face globally
 *               rather than remembered per table.
 *
 * What this is *not*: the interface used to be monospaced end to end. It had a
 * look and it cost legibility — a monospaced Turkish sentence is measurably
 * slower to read — and the grid it bought is now the layout's job.
 */

@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@600;700&family=Barlow:ital,wght@0,400;0,500;0,600;0,700;1,400&family=JetBrains+Mono:wght@400;500;700&display=swap');

:root {
	color-scheme: dark;

	/*
	 * -- Surfaces ----------------------------------------------------------
	 *
	 * Four steps, all warm. The neutrals carry a little red and yellow rather
	 * than being grey, because a gold accent on a *cold* near-black reads as two
	 * unrelated colours sitting next to each other, and on a warm one it reads as
	 * light on metal. The hue is the same across all four and only lightness
	 * moves, so a card on a card on the page still reads as a stack.
	 */
	--void:  #0a0806;   /* the page itself */
	--plate: #16110d;   /* a card, a banded table row */
	--forge: #201811;   /* something raised on a card: a chip, an input */
	--seam:  #2f2419;   /* the line between two things */
	--seam-soft: #1d1610;

	/*
	 * -- The accent --------------------------------------------------------
	 *
	 * One hot colour, and it is the game's own. Gold rather than the red this
	 * started as, for two reasons pointing the same way: the client's interface is
	 * gold and bronze on near-black, so a player already reads gold as "this
	 * game"; and Karus is red, so an accent in that family made the chrome look
	 * like it had taken a side.
	 *
	 * `--gold-ink` is what goes *on* gold — near-black, not white. Gold at this
	 * lightness fails contrast under white text and passes comfortably under
	 * dark, and the one filled control on the page has to be unmistakable.
	 */
	--gold:      #e8b04a;
	--gold-hot:  #ffd479;
	--gold-deep: #a9781f;
	--gold-ink:  #17110a;
	/* The accent at low alpha, for a wash behind something rather than a fill. */
	--gold-veil: #e8b04a1f;

	/*
	 * -- The nations -------------------------------------------------------
	 *
	 * Not a design choice. Karus is red and El Morad is blue in the game, in its
	 * client, and in every panel a player already reads; swapping them would make
	 * a familiar page lie. They appear where the nation *is* the data — a name, a
	 * ladder bar, the war line — and nowhere else.
	 */
	--karus:   #e0453a;
	--elmorad: #4d8dff;
	--good:    #59d98b;
	--bad:     #f4544a;

	/*
	 * -- Text --------------------------------------------------------------
	 *
	 * Warm off-white, not white. Pure white on a warm near-black glares and makes
	 * the gold look dirty beside it; parchment sits with it.
	 */
	--parch:       #f1e6d2;
	--parch-dim:   #bcab8e;
	--parch-faint: #7d7160;

	/*
	 * -- Measure and rhythm ------------------------------------------------
	 *
	 * One measure and one gutter align every full-width band — masthead, main
	 * column, footer — so the page keeps a single left edge all the way down. The
	 * space scale is a 4px grid, and every margin in this file is one of these
	 * steps; that is what stops the vertical rhythm drifting section by section.
	 */
	--measure: 82rem;
	--gutter: 1.6rem;

	--s1: .25rem;
	--s2: .5rem;
	--s3: .75rem;
	--s4: 1rem;
	--s5: 1.5rem;
	--s6: 2rem;
	--s7: 3rem;
	--s8: 4.5rem;

	/*
	 * -- Radius ------------------------------------------------------------
	 *
	 * Two tiers, and the split carries meaning. **Data is square** — a table cell,
	 * an item square, a ladder row — because rounding a grid of numbers softens
	 * the one thing that should read as precise. **Containers are rounded**,
	 * because a card wants an edge whose shape you can see. One radius across both
	 * is what makes an interface look undesigned.
	 */
	--round-data: 3px;
	--round-card: 10px;
	--round-pill: 999px;

	/*
	 * -- Depth -------------------------------------------------------------
	 *
	 * A hairline and a shadow, no more. On a near-black page a shadow does almost
	 * nothing and a *lighter top edge* does the work: light falls from above, so
	 * the top border is what says a thing is raised.
	 */
	--lift: inset 0 1px 0 #ffffff0f;
	--drop: 0 1px 2px #00000059, 0 8px 24px -12px #000000a6;

	--display: 'Cinzel', 'Times New Roman', serif;
	--ui: 'Barlow', system-ui, -apple-system, sans-serif;
	--mono: 'JetBrains Mono', ui-monospace, monospace;

	/*
	 * -- Compatibility -----------------------------------------------------
	 *
	 * The names the rest of this file already uses, pointed at the scale above.
	 * Kept rather than renamed across three thousand lines in one pass: a rename
	 * that size is a diff nobody can review, and the values are what changed.
	 */
	--ink: var(--void);
	--slab: var(--plate);
	--rule: var(--seam);
	--rule-soft: var(--seam-soft);
	--accent: var(--gold);
	--accent-soft: var(--gold-hot);
	--bone: var(--parch);
	--ash: var(--parch-dim);
	--dim: var(--parch-faint);
	--round: var(--round-data);
}

* { box-sizing: border-box; }

/*
 * `clip`, not `hidden`.
 *
 * The full-bleed hero is `100vw` wide, and `100vw` counts the vertical
 * scrollbar — so the page ends up a few pixels wider than the window and grows
 * a horizontal scrollbar of its own. Something has to absorb that overhang.
 *
 * `overflow-x: hidden` would do it and would also turn this element into a
 * scroll container, which breaks `position: sticky` on the masthead. `clip`
 * cuts the overhang without creating one, so the bar keeps sticking.
 */
html { overflow-x: clip; }

body {
	margin: 0;
	background: var(--void);
	color: var(--parch);
	font: 400 16px/1.6 var(--ui);
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
}

a { color: inherit; }

/*
 * ── The three faces, assigned once ───────────────────────────────────────────
 *
 * Set here rather than repeated per component, so a heading is a heading because
 * of what it *is* and not because somebody remembered to name the family.
 *
 * `Cinzel` is caps-only by design — the face has no true lowercase worth setting
 * — so the letter-spacing and the transform travel with it. Anything wearing
 * `--display` is therefore already engraved and needs no further styling.
 */
h1, h2, h3, .display {
	font-family: var(--display);
	font-weight: 700;
	letter-spacing: .04em;
	text-transform: uppercase;
	/* Caps have no descenders, so the default line height leaves a gap under
	   every heading that reads as a mistake in the spacing. */
	line-height: 1.15;
	margin: 0;
}

/*
 * Every number, and it is not optional.
 *
 * A ladder is a column of figures, and proportional digits make `1` narrower
 * than `8` — so a column of national points comes out ragged and the eye cannot
 * compare two rows without reading both. `tabular-nums` fixes the advance width;
 * `slashed-zero` keeps a zero from reading as an O in an item name beside it.
 */
.num, .mono, td, time, output {
	font-family: var(--mono);
	font-variant-numeric: tabular-nums slashed-zero;
}

/*
 * ── The eyebrow ──────────────────────────────────────────────────────────────
 *
 * The small label above a block — "SIRALAMA", "SON HABERLER". It earns a name
 * because it appears on every page and because the temptation is to reach for
 * the display face: engraved caps at 11px are unreadable, and the whole point of
 * keeping Cinzel to four places is lost the moment it labels things too.
 */
.eyebrow {
	font: 600 .72rem/1 var(--ui);
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var(--parch-faint);
}

/*
 * Focus, once, for everything.
 *
 * Two rings so it is visible against both the page and a gold fill: the inner
 * dark line separates the outer gold one from whatever is underneath.
 */
:focus-visible {
	outline: 2px solid var(--gold-hot);
	outline-offset: 2px;
	border-radius: var(--round-data);
}

/*
 * Nobody who asked for less motion gets any.
 *
 * `!important` because this has to beat a component that sets its own
 * transition, and a preference the interface overrules is not a preference. The
 * 3D viewer is deliberately untouched — it is the content, not decoration, and a
 * character that does not turn is a broken feature rather than a calmer page.
 */
@media (prefers-reduced-motion: reduce) {
	*:not(canvas) {
		animation-duration: .01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: .01ms !important;
		scroll-behavior: auto !important;
	}
}

/* ── Chrome ──────────────────────────────────────────────────── */

/*
 * The masthead's layout lives with the navigation further down; all this rule
 * does is draw the line under it. Two `.masthead` blocks setting `display` from
 * different parts of the sheet is how the bar ended up shrink-wrapped around its
 * own contents and out of line with the page.
 */
.masthead {
	border-bottom: 1px solid var(--rule);
}

.brand {
	display: inline-flex;
	align-items: center;
	text-decoration: none;
	color: var(--accent);
}

/*
 * The blades take the link's colour, so the mark answers to `--accent` and to
 * hover without a second rule anywhere.
 */
.logo { height: 30px; width: auto; display: block; }

.logo-word {
	fill: var(--bone);
	font-family: var(--display);
	font-weight: 700;
	font-size: 19px;
	letter-spacing: .16em;
}

.logo-x { fill: var(--accent); }

/* The suffix rides below the cap line at half size — a subtitle, not a second
   word competing with the name. */
.logo-tail {
	fill: var(--ash);
	font-family: var(--ui);
	font-weight: 500;
	font-size: 10px;
	letter-spacing: .3em;
}

.brand:hover .logo-word { fill: #fff; }

main {
	max-width: var(--measure);
	margin: 0 auto;
	padding: 2.2rem var(--gutter) 4rem;
}

/*
 * The rule spans the window; the columns under it share the page's measure.
 *
 * The padding used to sit on `.footer` and the max-width on the grid inside it,
 * which made the footer's content exactly one gutter wider on each side than the
 * main column — the columns started left of the character stage above them. Both
 * bands now measure the same way the masthead does: full-bleed background, inner
 * box capped at `--measure` with the gutter inside it.
 */
.footer {
	padding: 2.4rem 0 3rem;
	border-top: 1px solid var(--rule);
	color: var(--ash);
	font-size: .85rem;
}

/* ── The dossier ─────────────────────────────────────────────── */

/*
 * The stage is given a ceiling rather than a share of the row.
 *
 * A fractional split ties the figure's size to the width of the sheet beside
 * it, so adding a column of statistics shrinks the character — which is the
 * wrong dependency. 748px is the reference panel's own stage width, and at the
 * frame's 10:9 that is 748 × 673, within a couple of pixels of theirs.
 */
.dossier {
	display: grid;
	grid-template-columns: minmax(0, 680px) minmax(21rem, 1fr);
	gap: 1.8rem;
	align-items: start;
}

@media (max-width: 62rem) {
	.dossier { grid-template-columns: 1fr; gap: 1.6rem; }
}

/*
 * The stage only holds still when there is a second column for the reader to
 * scroll past it. Stacked, there is nothing beside it — so a sticky stage stops
 * halfway up and the sheet scrolls straight over the top of it, which is what a
 * sticky element does when its containing block is the whole page rather than a
 * column. Keeping the rule behind the same breakpoint as the two-column grid
 * ties the two facts together instead of leaving them to agree by accident.
 */
@media (min-width: 62.01rem) {
	.stage-wrap {
		position: sticky;
		top: 1.6rem;
	}
}

.stage {
	position: relative;
	/*
	 * Taller than the reference's 748 × 675, and the reason is the wings.
	 *
	 * 10:9 was measured off the reference panel and the argument for it still
	 * holds — the backdrop is a room, and a tall narrow crop shows floor and
	 * ceiling instead of the space to either side that makes it read as one. What
	 * that measurement did not account for is what a character on *this* server
	 * actually wears: a costume wing set stands a good half-metre above the head,
	 * and at 10:9 the tips were cut off by the frame's top edge.
	 *
	 * 1:1 buys back that headroom without turning the room into a corridor. The
	 * alternative — pulling the camera back — was tried in the head and rejected:
	 * it shrinks the figure to fit its tallest accessory, so a character with no
	 * wings is drawn small for a reason the reader cannot see.
	 */
	aspect-ratio: 1 / 1;
	overflow: hidden;
	background: #000;
	border: 1px solid var(--rule);
	/* A hairline along the top, the way the game frames a panel. */
	box-shadow: inset 0 1px 0 rgb(255 255 255 / .06);
}

/*
 * The canvas fills its frame and is never allowed to size it.
 *
 * `WebGLRenderer.setSize(w, h, false)` sets the canvas *attributes* to
 * `size x devicePixelRatio` and deliberately leaves the CSS alone, so an
 * unstyled canvas lays out at its pixel count — two or three times its frame on
 * a phone. `aspect-ratio` states a preferred height, not a maximum, so the
 * frame stretches to fit it, the `ResizeObserver` reads the taller frame, and
 * the next `setSize` multiplies again: the podium grew down the page without
 * end and took the tab's memory with it. Desktop never showed it because at a
 * ratio of 1 the first pass already fits.
 *
 * Taking the canvas out of flow closes the loop at the source — it cannot
 * contribute a single pixel to its parent's height whatever it is set to.
 */
.stage canvas,
.pedestal-stage canvas {
	position: absolute;
	inset: 0;
	z-index: 10;
	display: block;
	width: 100%;
	height: 100%;
}

.stage-error {
	position: absolute;
	inset: auto 0 0 0;
	z-index: 20;
	margin: 0;
	padding: .7rem .9rem;
	background: rgb(0 0 0 / .78);
	border-top: 1px solid var(--rule);
	font-size: .85rem;
}

/* ── Nameplate ───────────────────────────────────────────────── */

.plate { border-bottom: 1px solid var(--rule); padding-bottom: 1.2rem; }

.plate-line {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: .3rem;
	margin: 0;
	font-size: .82rem;
	letter-spacing: .13em;
	text-transform: uppercase;
	color: var(--ash);
}

.plate-line .sep { color: var(--rule); margin: 0 .25rem; }
.nation-karus { color: var(--karus); }
.nation-elmorad { color: var(--elmorad); }

.plate-name {
	margin: .5rem 0 .7rem;
	font-family: var(--display);
	font-weight: 600;
	font-size: clamp(1.9rem, 4.2vw, 2.9rem);
	line-height: 1.05;
	letter-spacing: .04em;
	/* Long names must shrink the tracking, not overflow the column. */
	overflow-wrap: anywhere;
}

.plate-meta {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 1rem;
	margin: 0;
	font-size: .95rem;
	color: var(--ash);
}

.plate-meta .level strong {
	font-family: var(--mono);
	font-weight: 700;
	color: var(--bone);
}

.clan { display: inline-flex; align-items: center; gap: .45rem; color: var(--bone); }

.clan-mark {
	/* 32 px art; smoothing it on the way down turns a crisp badge into a smear. */
	image-rendering: pixelated;
}

/* ── National points ─────────────────────────────────────────── */

.np { padding: 1.4rem 0; border-bottom: 1px solid var(--rule); }

.np-label {
	margin: 0;
	font-size: .74rem;
	letter-spacing: .22em;
	text-transform: uppercase;
	color: var(--ash);
}

.np-figure {
	margin: .25rem 0 0;
	font-family: var(--display);
	font-weight: 500;
	font-size: clamp(2rem, 5vw, 2.8rem);
	line-height: 1;
	letter-spacing: .02em;
	color: var(--accent);
	font-variant-numeric: tabular-nums;
}

/* ── Blocks ──────────────────────────────────────────────────── */

/*
 * The vertical rhythm, tightened to the editors'.
 *
 * A dossier is six blocks and the reader wants them at once — the gear beside
 * the statistics beside the record — so the space between them is the space it
 * takes to see they are separate and no more. The rule between two blocks does
 * the separating; padding was doing it twice.
 */
.block { padding: 1rem 0; border-bottom: 1px solid var(--rule-soft); }
.block:last-child { border-bottom: 0; }

/*
 * A section label, at the size a label should be.
 *
 * It was set at .74rem in near-white, which is close enough to the numbers
 * under it to compete with them. The heading is signposting: it should be
 * findable and then get out of the way, so it is smaller, dimmer and spaced —
 * the same treatment the editors give theirs.
 */
.block-title {
	margin: 0 0 .55rem;
	font-size: .66rem;
	font-weight: 600;
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--dim);
}

/*
 * The paper doll.
 *
 * Three squares across, in the record's own order, which is what turns a flat
 * list into the arrangement every player already knows: ears beside the head,
 * hands beside the belt, rings beside the legs. The names are not printed —
 * fourteen names in a grid is a wall of text, and the game does not print them
 * either. The icon is the label; the card tells the rest.
 *
 * Every square is framed the same way. The frames used to be tinted by upgrade
 * level, which turned the grid into a heat map of someone's investment — a nice
 * idea and the wrong one here: the icons are the game's own art, and colouring
 * the sockets around them competes with it. The plus level is on the card, where
 * a reader asks for it.
 */

/*
 * The square is bigger than the icon it holds, and that is the whole point.
 *
 * The client's squares are 53px around a 45px icon, and the eight pixels are
 * what you actually see: the frame reads as a socket the item sits in. Sizing
 * the square to the icon instead — which is what this did first — hides the
 * border underneath the art, and a grid of borderless pictures stops looking
 * like an inventory at all.
 */
.doll {
	--icon: 45px;
	--frame: 4px;

	display: grid;
	grid-template-columns: repeat(var(--cols, 3), calc(var(--icon) + var(--frame) * 2));
	gap: 4px;
	justify-content: start;
	margin: 0;
	padding: 0;
	list-style: none;
}

.cell {
	position: relative;
	width: calc(var(--icon) + var(--frame) * 2);
	height: calc(var(--icon) + var(--frame) * 2);
	display: flex;
	align-items: center;
	justify-content: center;
	background: rgb(255 255 255 / .03);
	border: 1px solid var(--rule);
	border-radius: var(--round);
	/* A hairline of light inside the frame, so it reads as a bevel rather than
	   as a flat outline. */
	box-shadow: inset 0 0 0 1px rgb(255 255 255 / .04);
	cursor: default;

	/*
	 * The platform's press-and-hold does not apply here.
	 *
	 * A long press on an image is a system gesture — Android offers to open it
	 * in a new tab, iOS raises the preview sheet — and every square holds one.
	 * On this grid the hold is meant to open the item card, so the callout and
	 * the text selection that comes with it are turned off. Only on the square:
	 * an image anywhere else on the site keeps its normal behaviour.
	 */
	-webkit-touch-callout: none;
	-webkit-user-select: none;
	user-select: none;
}

/* Dragged off, an icon becomes a file the browser wants to do something with. */
.cell img { -webkit-user-drag: none; user-select: none; pointer-events: none; }

.cell-icon {
	position: relative;
	display: block;
	width: var(--icon, 45px);
	height: var(--icon, 45px);
	image-rendering: pixelated;
}

/* The item's name, sitting under the icon and covered by it when one arrives. */
.cell-fallback {
	position: absolute;
	inset: 2px;
	display: flex;
	align-items: center;
	justify-content: center;
	text-align: center;
	font-size: .5rem;
	line-height: 1.15;
	letter-spacing: .02em;
	color: var(--ash);
	overflow: hidden;
	pointer-events: none;
}

/*
 * An empty square stays a square and says nothing.
 *
 * It carried its slot name for a while — "PAULDRON", "RIGHT HAND" — and at 45px
 * that is a word set at half the size of everything else, repeated eleven times
 * down a grid whose whole point is that the arrangement already says which slot
 * is which. The name survives for a screen reader, where the shape does not
 * carry.
 */
.cell.is-empty {
	background: rgb(255 255 255 / .015);
}

.sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

.cell:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* The two wings sit side by side, costume first, as the game arranges them. */
.racks {
	display: flex;
	align-items: flex-start;
	gap: 1.1rem;
	flex-wrap: wrap;
}

/*
 * On a phone the two racks stay side by side, and the icon gives way.
 *
 * Wrapped, the costume column drops under the equipment one and the block
 * becomes twice as tall as the stage above it — a paper doll read as two
 * separate lists. They fit at 377px already; what they do not survive is a
 * 320px screen, so rather than `nowrap` on its own the icon is sized from the
 * space actually available and only shrinks when it has to.
 *
 * The arithmetic: six columns across both racks, each square the icon plus its
 * 4px frame and 1px border on each side, plus two 4px gaps inside each rack and
 * one between them. `min()` keeps the full 45px wherever it fits.
 */
@media (max-width: 30rem) {
	.racks {
		flex-wrap: nowrap;
		gap: .5rem;
	}

	.doll {
		--icon: clamp(
			28px,
			calc((100vw - var(--gutter) * 2 - .5rem - 76px) / 6),
			45px
		);
		min-width: 0;
	}
}

/* The gap in the client's own square numbering. It holds the grid's shape and
   is not a slot, so it takes no border and answers to nothing. */
.cell.is-void {
	background: none;
	border-color: transparent;
}

/* ── Stats ───────────────────────────────────────────────────── */

.stats {
	display: grid;
	grid-template-columns: repeat(5, minmax(0, 1fr));
	gap: 1px;
	margin: 0 0 1px;
	background: var(--rule);
	border: 1px solid var(--rule);
}

.stat {
	background: var(--slab);
	padding: .7rem .5rem;
	text-align: center;
}

.stat dt {
	font-size: .68rem;
	letter-spacing: .14em;
	color: var(--ash);
}

.stat dd {
	margin: .2rem 0 0;
	font-family: var(--mono);
	font-weight: 500;
	font-size: 1.15rem;
	font-variant-numeric: tabular-nums;
}

/* ── The item card ───────────────────────────────────────────── */

/*
 * The item card, to the reference panel's own measurements — 340px wide, the
 * name at 16px with the type and class right-aligned beside it, statistics at
 * 11.5px, and hairline rules at 8% white rather than a visible border.
 */
.item-card {
	position: fixed;
	z-index: 100;
	width: min(340px, calc(100vw - 20px));
	max-height: calc(100vh - 24px);
	overflow-y: auto;
	padding: 10px 12px 12px;
	/*
	 * Translucent, as the game's is.
	 *
	 * Nearly opaque was the safe-looking choice and it is what makes the card
	 * read as a web tooltip pasted over a screenshot: the game's panel lets the
	 * world show faintly through, so it sits *in* the scene. The blur keeps the
	 * text legible over whatever is behind it, which plain transparency does
	 * not.
	 */
	background: rgb(4 4 6 / .82);
	backdrop-filter: blur(3px);
	border: 1px solid var(--rule);
	box-shadow: 0 12px 34px rgb(0 0 0 / .5);
	pointer-events: none;
}

.ic-head {
	display: flex;
	gap: 8px;
	align-items: flex-start;
	margin-bottom: 5px;
}

.ic-icon {
	flex: 0 0 auto;
	width: 45px;
	height: 45px;
	display: flex;
	align-items: center;
	justify-content: center;
	border: 1px solid var(--rule);
	background: rgb(255 255 255 / .03);
	border-radius: 4px;
}

.ic-icon img { image-rendering: pixelated; }

.ic-head > div { flex: 1; min-width: 0; }

.ic-name {
	margin: 0;
	font-size: 16px;
	font-weight: 700;
	line-height: 1.2;
}

/* Right-aligned under the name, so the eye reads down the left edge for the
   name and down the right for what kind of thing it is. */
.ic-type, .ic-class {
	margin: 0 0 4px;
	font-size: 11px;
	letter-spacing: .5px;
	text-align: right;
}

/* The `Kind` line — "Dagger", "One-handed Sword". The game prints it white
   whatever the item is, so it does not follow the rarity colour above it. */
.ic-class { color: #fff; }

.ic-dur {
	margin: 0;
	color: #fff;
	font-size: 11px;
	line-height: 1.6;
}

/* The game's own threshold: under a quarter left, the line turns red. */
.ic-dur.is-low { color: #ff3c3c; }

.ic-rule {
	height: 0;
	margin: 0 0 10px;
	border-bottom: 1px solid rgb(255 255 255 / .08);
}

.ic-stats {
	font-size: 11.5px;
	line-height: 1.6;
	color: #ddd;
}

.ic-line { margin: 0; }

/*
 * The client's two stat colours, and there are only two.
 *
 * White covers what the item does by itself — attack, speed, range, weight, the
 * base defence, and the requirements. The green is everything the item *adds*:
 * the per-weapon defences, the elemental damages, every bonus and every
 * resistance. Which line falls where is not derivable from its text, which is
 * why guessing produced the wrong answer twice; it comes from the tooltip's own
 * `SetColor` calls.
 *
 * A third colour exists in the game, `#FF3C3C`, and is deliberately unused here:
 * it marks a requirement the *reader* fails, and a panel has no reader.
 */
.ic-line { color: #fff; }
.ic-line.is-bright { color: #80ff00; }

/* Coloured per item by the server; see the card renderer. */
.ic-grade {
	margin: 6px 0;
	font-size: 11px;
	font-weight: 600;
	text-align: center;
}

.ic-remark {
	margin: 0;
	font-size: 12px;
	line-height: 1.4;
	color: #ccc;
	text-align: center;
	font-style: italic;
}


/* ── Navigation ──────────────────────────────────────────────── */

/*
 * A portal masthead, not a ladder's title bar.
 *
 * Nine ranking links in a row said the whole site was a ranking, which was the
 * complaint and a fair one. They now live in one menu, and the bar carries the
 * five destinations plus the one thing a visitor who has never played is here
 * for: the client.
 */
.masthead {
	position: sticky;
	top: 0;
	z-index: 40;
	padding: 0;
	background: rgb(8 8 10 / .92);
	backdrop-filter: blur(10px);
}

/*
 * One measure for the whole site.
 *
 * The rule spans the window; everything that sits on it lines up with the
 * content below. `--measure` and `--gutter` are declared once so the masthead,
 * the page and the footer cannot drift apart — which is exactly what happened
 * when each carried its own max-width.
 */
.masthead-inner {
	display: flex;
	align-items: center;
	gap: 1.6rem;
	flex-wrap: wrap;
	max-width: var(--measure);
	margin: 0 auto;
	padding: 0 var(--gutter);
	min-height: 58px;
}

.brand {
	display: inline-flex;
	align-items: center;
	gap: .55rem;
}

/* A blade, not a logo: two strokes of the accent, the taller one leaning. */
.brand-mark {
	width: 10px;
	height: 18px;
	background: var(--accent);
	clip-path: polygon(0 100%, 45% 0, 55% 0, 100% 100%);
}

.nav {
	display: flex;
	align-items: center;
	gap: .3rem;
	flex-wrap: wrap;
	margin-right: auto;
}

.nav-link {
	position: relative;
	display: inline-flex;
	align-items: center;
	gap: .35rem;
	padding: 1.05rem .7rem;
	font-size: .74rem;
	letter-spacing: .13em;
	text-transform: uppercase;
	text-decoration: none;
	color: var(--ash);
	white-space: nowrap;
}

.nav-link:hover { color: var(--bone); }
.nav-link.is-here,
.menu.is-here > .nav-link { color: var(--bone); }

/* The current page is marked under the word rather than behind it — a filled
   pill would compete with the bars, which are the page's one loud device. */
.nav-link.is-here::after,
.menu.is-here > .nav-link::after {
	content: '';
	position: absolute;
	inset: auto .7rem 0 .7rem;
	height: 2px;
	background: var(--accent);
}

/* ── The ranking menu ────────────────────────────────────────── */

.menu { position: relative; }

.menu-arrow {
	width: 0;
	height: 0;
	border: 3px solid transparent;
	border-top-color: currentColor;
	transform: translateY(1px);
	transition: transform .15s;
}

.menu-panel {
	position: absolute;
	top: 100%;
	left: 0;
	z-index: 50;
	display: grid;
	grid-template-columns: 15rem 10rem;
	gap: 1.6rem;
	padding: 1.2rem 1.4rem;
	background: #101014;
	border: 1px solid var(--rule);
	box-shadow: 0 24px 48px rgb(0 0 0 / .6);

	/* Hidden without being unreachable: `visibility` keeps it out of the tab
	   order until it opens, which `opacity: 0` alone would not. */
	opacity: 0;
	visibility: hidden;
	transform: translateY(-6px);
	transition: opacity .14s, transform .14s, visibility .14s;
}

.menu:hover .menu-panel,
.menu:focus-within .menu-panel {
	opacity: 1;
	visibility: visible;
	transform: none;
}

.menu:hover .menu-arrow,
.menu:focus-within .menu-arrow { transform: translateY(1px) rotate(180deg); }

.menu-head {
	margin: 0 0 .6rem;
	font-size: .64rem;
	letter-spacing: .22em;
	text-transform: uppercase;
	color: var(--dim);
}

.menu-link {
	display: block;
	padding: .45rem .5rem;
	margin: 0 -.5rem;
	text-decoration: none;
	color: var(--ash);
}

.menu-link:hover { background: rgb(255 255 255 / .05); }
.menu-link.is-here { color: var(--bone); box-shadow: inset 2px 0 0 var(--accent); }

.menu-name {
	display: block;
	font-size: .84rem;
	font-weight: 500;
	color: var(--bone);
}

.menu-note {
	display: block;
	margin-top: .1rem;
	font-size: .7rem;
	color: var(--dim);
}

.find-input {
	width: 12rem;
	padding: .42rem .6rem;
	background: rgb(255 255 255 / .04);
	border: 1px solid var(--rule);
	color: var(--bone);
	font: 400 .82rem var(--ui);
}

.find-input::placeholder { color: var(--dim); }
.find-input:focus { outline: none; border-color: var(--accent); }

/* The one filled control in the chrome. Everything else in the masthead is a
   word; this is the only thing shaped like a button, so it reads as the action. */
.play {
	padding: .45rem 1.1rem;
	background: var(--accent);
	/* Dark on gold: white at this weight and size fails to separate. */
	color: var(--ink);
	font-size: .74rem;
	font-weight: 700;
	letter-spacing: .16em;
	text-transform: uppercase;
	text-decoration: none;
	white-space: nowrap;
}

.play:hover { background: var(--accent-soft); }

.skip {
	position: absolute;
	left: -9999px;
}

.skip:focus {
	left: 1rem;
	top: .6rem;
	z-index: 60;
	padding: .5rem .8rem;
	background: var(--accent);
	color: var(--ink);
	font-size: .8rem;
}

@media (max-width: 60rem) {
	.menu-panel { grid-template-columns: 1fr; gap: 1rem; }
	.find { order: 3; }
}

/*
 * ── The war line ─────────────────────────────────────────────────────────────
 *
 * The signature. Two centimetres of border carrying the live share of national
 * points between the two nations, sitting as the masthead's bottom edge on every
 * page and repeated with labels on the front page.
 *
 * One custom property drives it — `--karus-share`, a percentage written by the
 * partial. The bar and the numbers under it read from the same value, so they
 * cannot disagree, and a server whose balance shifts moves the line without a
 * single rule here changing.
 */
.warline {
	--karus-share: 50%;
}

.warline-bar {
	display: flex;
	height: 2px;
	/* The seam shows through where neither side has scored, so a bar that does
	   not add up to 100% looks unfinished rather than wrong. */
	background: var(--seam);
}

.warline-side {
	/*
	 * A gradient, not a flat fill, and it runs *towards the join*.
	 *
	 * At 2px a flat colour reads as a coloured line and nothing more. Fading each
	 * side up as it approaches the middle makes the meeting point the brightest
	 * part of the bar, which is where the information is: the front is where the
	 * two sides touch.
	 */
	display: block;
	transition: flex-basis .6s cubic-bezier(.22, 1, .36, 1);
}

.warline-side--karus {
	flex: 0 0 var(--karus-share);
	background: linear-gradient(90deg, color-mix(in oklab, var(--karus) 35%, transparent), var(--karus));
}

.warline-side--elmorad {
	flex: 1 1 auto;
	background: linear-gradient(90deg, var(--elmorad), color-mix(in oklab, var(--elmorad) 35%, transparent));
}

/*
 * ── The front ────────────────────────────────────────────────────────────────
 *
 * Two nations facing each other across a contested seam. Replaces a percentage,
 * a bar and a percentage, which read as a progress indicator — and nothing here is
 * progressing towards anything. They are pushing against each other.
 *
 * One number drives the whole thing: `--karus-share`, written by the partial. The
 * seam, the marker and both territories read from it, so they cannot disagree.
 */
.warline--full {
	display: grid;
	gap: var(--s4);
	padding: 0;
}

/*
 * Three columns: a nation, the lead, a nation. The middle one takes only what it
 * needs, so the two sides sit as far apart as the band allows — which is the
 * point. A confrontation drawn close together is a comparison.
 */
.front {
	display: grid;
	grid-template-columns: 1fr auto 1fr;
	gap: var(--s4);
	align-items: start;
}

.front-nation { display: grid; gap: var(--s1); }

/* El Morad mirrored, so the pair reads outward from the middle. */
.front-nation--elmorad { text-align: right; justify-items: end; }

.front-name {
	display: flex;
	align-items: center;
	gap: var(--s2);
	margin: 0;
	font-size: 1.05rem;
	letter-spacing: .06em;
	color: var(--side, var(--parch));
}

.front-nation--elmorad .front-name { flex-direction: row-reverse; }

.front-nation--karus { --side: var(--karus); }
.front-nation--elmorad { --side: var(--elmorad); }

/*
 * The game's own 16px crest, shown at 24 and deliberately not smoothed.
 *
 * `pixelated` because these are 2004 icons and scaling them with interpolation
 * gives a soft grey smudge. Left hard they read as pixel art, which on a site about
 * a game of this vintage is the correct register — and players recognise them,
 * which is why a redrawn substitute would be worse however neat.
 */
.front-name .mark {
	width: 24px;
	height: 24px;
	image-rendering: pixelated;
}

/*
 * The percentage, and the one place a number is set large.
 *
 * `clamp` off the viewport so it is the same weight of statement on a phone as on
 * a desktop. The per-cent sign is set smaller and lighter than the figure: at the
 * same size it takes as much attention as the number it qualifies.
 */
.front-share {
	margin: 0;
	font-size: clamp(2rem, 5.5vw, 3.4rem);
	font-weight: 700;
	line-height: .95;
	color: var(--parch);
}

.front-pct {
	font-size: .42em;
	font-weight: 500;
	color: var(--parch-faint);
	margin-left: .12em;
}

.front-detail {
	margin: 0;
	font-size: .82rem;
	color: var(--parch-faint);
}

.front-dot { opacity: .5; }

/* A side with nobody on the board says so, in italics, so it reads as a note
   rather than as a value. */
.front-empty { font-style: italic; }

/*
 * ── The lead ─────────────────────────────────────────────────────────────────
 *
 * Between the two names, and set on the nation's colour that holds it. The number
 * a player feels; the old version had both halves of it and never did the
 * subtraction.
 */
.front-lead {
	display: grid;
	gap: 2px;
	justify-items: center;
	padding: 0 var(--s3);
	text-align: center;
}

.front-lead-label {
	margin: 0;
	font: 600 .66rem/1.2 var(--ui);
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--parch-faint);
}

.front-lead-figure {
	margin: 0;
	font-size: 1rem;
	font-weight: 700;
	color: var(--parch);
}

.front-lead-figure.nation-karus { color: var(--karus); }
.front-lead-figure.nation-elmorad { color: var(--elmorad); }

/*
 * ── The ground ───────────────────────────────────────────────────────────────
 *
 * Where the two meet. Not a bar with a fill: two territories, each holding its
 * share, with a marker standing on the split.
 */
.ground {
	position: relative;
	display: flex;
	height: 26px;
	background: var(--void);
	box-shadow: inset 0 1px 0 #00000099, inset 0 -1px 0 #ffffff0d;
	overflow: hidden;
}

/*
 * The crenellation.
 *
 * The territory's top edge is a battlement — merlon, embrasure, merlon. This is a
 * game whose centrepiece is a weekly castle siege, so it is the shape of the thing
 * both sides are fighting over rather than an ornament from elsewhere. It also does
 * real work: a notched edge reads as *ground held*, where a smooth fill reads as a
 * loading bar, and that was the whole problem with what stood here.
 *
 * Two layers make it. A repeating gradient cuts the merlons out of the top eight
 * pixels; a flat fill holds the wall below them. `9px` merlon on a `16px` pitch is
 * the proportion the game's own battlements use.
 */
.ground-side {
	position: relative;
	display: block;
	transition: flex-basis .9s cubic-bezier(.22, 1, .36, 1);
	background-repeat: no-repeat, no-repeat;
	background-size: 100% 8px, 100% calc(100% - 8px);
	background-position: top left, bottom left;
}

.ground-side--karus {
	flex: 0 0 var(--karus-share);
	background-image:
		repeating-linear-gradient(
			90deg,
			var(--karus) 0 9px,
			transparent 9px 16px
		),
		linear-gradient(180deg, var(--karus), color-mix(in oklab, var(--karus) 55%, #000));
}

/*
 * El Morad's merlons are aligned to its *own* edge, so the pattern runs outward
 * from each side rather than continuing across the seam. Two armies do not share a
 * wall.
 */
.ground-side--elmorad {
	flex: 1 1 auto;
	background-image:
		repeating-linear-gradient(
			270deg,
			var(--elmorad) 0 9px,
			transparent 9px 16px
		),
		linear-gradient(180deg, var(--elmorad), color-mix(in oklab, var(--elmorad) 55%, #000));
	background-position: top right, bottom right;
}

/*
 * The seam: a lozenge standing exactly on the split.
 *
 * The page's own motif — the plaque's diamond, the plates' fittings — used here as
 * the front-line marker, so the one place the site draws attention to is drawn in
 * the language the rest of it already speaks.
 *
 * It slides in from the left on load. The animation is *meaningful* rather than
 * decorative: it shows the line arriving at where it stands today. `left` animates
 * rather than `transform` because the resting position is a percentage of the
 * parent and a transform would need the parent's width in JavaScript to match it.
 */
.ground-seam {
	position: absolute;
	top: 50%;
	left: var(--karus-share);
	width: 13px;
	height: 13px;
	margin: -7px 0 0 -7px;
	background: var(--gold-hot);
	border: 1px solid var(--gold-ink);
	transform: rotate(45deg);
	box-shadow: 0 0 0 3px #0a0806, 0 0 14px 2px color-mix(in oklab, var(--gold) 60%, transparent);
	animation: seam-arrive 1.1s cubic-bezier(.22, 1, .36, 1) both;
}

@keyframes seam-arrive {
	from { left: 0; opacity: 0; }
	to   { left: var(--karus-share); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
	.ground-seam { animation: none; }
	.ground-side { transition: none; }
}

/*
 * On a phone the three columns become two rows with the lead between them: the
 * percentages stay side by side, because comparing them is the whole job, and only
 * the lead moves. Stacking all three would put the two numbers a screen apart.
 */
@media (max-width: 40rem) {
	.front {
		grid-template-columns: 1fr 1fr;
		gap: var(--s2) var(--s3);
	}

	.front-lead {
		grid-column: 1 / -1;
		grid-row: 2;
		padding: var(--s2) 0 0;
		border-top: 1px solid var(--seam-soft);
	}

	.front-share { font-size: clamp(1.7rem, 11vw, 2.4rem); }
}

/* ── The war standing ────────────────────────────────────────── */

.war { margin-bottom: 2.6rem; }

.war-title {
	margin: 0 0 1rem;
	font-size: .74rem;
	font-weight: 600;
	letter-spacing: .24em;
	text-transform: uppercase;
	color: var(--ash);
}

/*
 * One bar, split where the two nations meet.
 *
 * The page's thesis: a ranking in this game is never neutral, because every
 * point on it was taken from the other side. So the front page opens on the
 * standing and the ladder underneath explains it.
 */
.war-bar {
	display: flex;
	height: 10px;
	overflow: hidden;
	background: var(--slab);
	border: 1px solid var(--rule);
}

.war-side { display: block; height: 100%; }
.war-side.is-karus { width: calc(var(--share) * 100%); background: var(--karus); }
.war-side.is-elmorad { flex: 1; background: var(--elmorad); }

.war-legend {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 1.5rem;
	margin-top: 1.1rem;
}

.war-nation:last-child { text-align: right; }

.war-share {
	margin: 0;
	font-family: var(--display);
	font-weight: 700;
	font-size: clamp(2.4rem, 6vw, 3.6rem);
	line-height: .95;
	letter-spacing: -.02em;
	font-variant-numeric: tabular-nums;
}

.war-share span { font-size: .4em; margin-left: .1em; }
.war-nation.is-karus .war-share { color: var(--karus); }
.war-nation.is-elmorad .war-share { color: var(--elmorad); }

.war-name {
	margin: .25rem 0 0;
	font-size: .8rem;
	letter-spacing: .2em;
	text-transform: uppercase;
}

.war-detail { margin: .2rem 0 0; font-size: .8rem; color: var(--ash); }

/* ── Boards ──────────────────────────────────────────────────── */

.columns {
	display: grid;
	grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
	gap: 2.4rem;
	align-items: start;
}

@media (max-width: 62rem) {
	.columns { grid-template-columns: 1fr; }
}

.board-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 1rem;
	padding-bottom: .5rem;
	border-bottom: 1px solid var(--rule-soft);
	margin-bottom: .2rem;
}

.board-title {
	margin: 0;
	font-size: .78rem;
	font-weight: 600;
	letter-spacing: .22em;
	text-transform: uppercase;
}

.board.is-full .board-title {
	font-family: var(--display);
	font-size: 1.25rem;
	letter-spacing: .02em;
	text-transform: none;
}

.board-count { margin: 0; font-size: .7rem; color: var(--dim); }

.board-more {
	font-size: .74rem;
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--ash);
	text-decoration: none;
}

.board-more:hover { color: var(--accent); }

.empty {
	padding: 2.4rem 0;
	color: var(--ash);
	font-size: .92rem;
	max-width: 34rem;
}

.empty strong { color: var(--bone); }

/* ── The ladder ──────────────────────────────────────────────── */

.ladder { display: flex; flex-direction: column; }

/*
 * The bar is the row's background, not an element beside it.
 *
 * `--fill` is this entry's share of the leader and the nation supplies the
 * colour, so one strip carries both facts at once: how steep the top of the
 * ladder is, and who owns it. Put in a column of its own it would be a chart
 * next to a table; put behind the row it *is* the ranking.
 */
.entry {
	position: relative;
	display: grid;
	/*
	 * Tighter than it was, on purpose.
	 *
	 * A ladder is read by running down it, and the thing being compared is the
	 * bar: the closer the rows sit, the more of the shape is in one glance.
	 * Padding was buying air between entries that the rule between them already
	 * provides. Twenty-five rows now fit where eighteen did.
	 */
	grid-template-columns: 2.4rem minmax(0, 1fr) auto;
	align-items: center;
	gap: .9rem;
	padding: .45rem .8rem;
	border-bottom: 1px solid var(--rule-soft);
	text-decoration: none;
	color: inherit;
	isolation: isolate;
}

.entry.is-clan { grid-template-columns: 2.4rem 28px minmax(0, 1fr) auto; }

.entry::before {
	content: '';
	position: absolute;
	inset: 0 auto 0 0;
	z-index: -1;
	width: calc(var(--fill, 0) * 100%);
	background: currentColor;
	opacity: .1;
	transition: opacity .15s;
}

.entry:hover::before { opacity: .2; }
.entry:hover .entry-name { color: var(--bone); }

/* The nation owns the bar's colour; the text stays neutral so a long list does
   not turn into two columns of shouting. */
.entry.nation-karus { color: var(--karus); }
.entry.nation-elmorad { color: var(--elmorad); }

/*
 * The place, set apart from the value.
 *
 * Both are numbers on the same line and they were the same size, so the eye had
 * to read the position of a figure to know which kind it was. Smaller and
 * dimmer settles it — a rank is the row's address, not its content — and
 * tabular figures keep the column straight whatever the digits are.
 */
.entry-rank {
	font-size: .72rem;
	color: var(--dim);
	font-variant-numeric: tabular-nums;
	text-align: right;
}

/* The first three keep the accent, because the top of a ladder is the reason
   most readers opened it. */
.ladder .entry:nth-child(-n + 3) .entry-rank { color: var(--accent); font-weight: 600; }

.entry-mark { width: 28px; height: 28px; }
.entry-mark img { image-rendering: pixelated; display: block; }

.entry-who { min-width: 0; }

.entry-name {
	display: block;
	font-weight: 600;
	font-size: .96rem;
	color: var(--bone);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/*
 * Explicit colours, not `currentColor`.
 *
 * The badge sits inside `.entry-name`, which sets its own colour — so
 * `background: currentColor` painted it in the name's near-white and then the
 * dark label on top vanished into it. `currentColor` is only useful where the
 * inherited colour is the one you actually want.
 */
.entry-badge {
	margin-left: .5rem;
	padding: .1rem .4rem;
	font-size: .62rem;
	font-style: normal;
	font-weight: 600;
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--ink);
	background: var(--ash);
	vertical-align: .1em;
}

/* The leader wears the nation's colour; everyone else's badge stays grey. */
.entry.nation-karus .entry-badge { background: var(--karus); }
.entry.nation-elmorad .entry-badge { background: var(--elmorad); }

.entry-meta {
	display: block;
	margin-top: .1rem;
	font-size: .76rem;
	color: var(--ash);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.entry-value {
	text-align: right;
	font-size: .96rem;
	font-weight: 500;
	color: var(--bone);
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}

.entry-unit {
	margin-left: .3rem;
	font-size: .68rem;
	color: var(--dim);
}

.dot { margin: 0 .35rem; color: var(--rule); }

/* ── Clan crest ──────────────────────────────────────────────── */

.crest {
	display: flex;
	align-items: center;
	gap: 1.4rem;
	padding-bottom: 1.6rem;
	margin-bottom: 2rem;
	border-bottom: 1px solid var(--rule);
}

.crest-mark { image-rendering: pixelated; flex: 0 0 auto; }

.crest-name {
	margin: 0;
	font-family: var(--display);
	font-weight: 700;
	font-size: clamp(1.8rem, 4vw, 2.6rem);
	line-height: 1.05;
	overflow-wrap: anywhere;
}

.crest-meta { margin: .4rem 0 0; font-size: .88rem; color: var(--ash); }

/*
 * The clan's own words, set apart from the panel's.
 *
 * Quoted and italic on a line of its own, so a reader can tell at a glance that
 * this sentence was typed by a player and the numbers above it were measured.
 */
.crest-notice {
	margin: .7rem 0 0;
	padding-left: .8rem;
	border-left: 2px solid var(--rule-soft);
	max-width: 44rem;
	font-size: .84rem;
	line-height: 1.5;
	color: var(--ash);
	font-style: italic;
	overflow-wrap: anywhere;
}

.crest-notice::before { content: '“'; }
.crest-notice::after { content: '”'; }

.notice {
	padding: 1.6rem;
	background: var(--slab);
	border: 1px solid var(--rule);
}

/* Icons shared by the nameplate: 32 px art shown small, so never smoothed. */
.mark {
	vertical-align: -.18em;
	margin-right: .35em;
	image-rendering: pixelated;
}

/* ── The set the character stands in ─────────────────────────── */

.stage-backdrop {
	position: absolute;
	inset: 0;
	z-index: 0;
	background: url(/img/MainBackground1.png) no-repeat center center;
	background-size: cover;
	box-shadow:
		inset 60px 0 80px -20px rgb(0 0 0 / .9),
		inset -60px 0 80px -20px rgb(0 0 0 / .9),
		inset 0 -50px 80px -20px rgb(0 0 0 / 1);
}

.stage-ray {
	position: absolute;
	top: -5%;
	left: 50%;
	width: 150%;
	height: 120%;
	z-index: 1;
	transform-origin: top center;
	mix-blend-mode: screen;
	pointer-events: none;
	background-repeat: no-repeat;
	background-position: top center;
	background-size: 100% 100%;
}

.stage-ray-1 {
	background-image: url(/img/god_rays1.png);
	animation: stage-ray-1 6s infinite alternate ease-in-out;
}

.stage-ray-2 {
	background-image: url(/img/god_rays2.png);
	animation: stage-ray-2 8s infinite alternate ease-in-out;
}

@keyframes stage-ray-1 {
	0%   { transform: translateX(-50%) rotate(-3deg); opacity: .7; filter: blur(2px); }
	50%  { opacity: 1; }
	100% { transform: translateX(-50%) rotate(3deg);  opacity: .7; filter: blur(3px); }
}

@keyframes stage-ray-2 {
	0%   { transform: translateX(-50%) rotate(4deg);  opacity: .9; filter: blur(3px); }
	50%  { opacity: .6; }
	100% { transform: translateX(-50%) rotate(-2deg); opacity: .9; filter: blur(2px); }
}

.stage-dust {
	position: absolute;
	inset: 0;
	z-index: 2;
	background: url(/img/dust.png) repeat;
	background-size: 256px 256px;
	opacity: .5;
	mix-blend-mode: screen;
	pointer-events: none;
	animation: stage-dust-far 15s linear infinite;
	mask-image: radial-gradient(ellipse 35% 70% at 50% 20%, #000 0%, transparent 100%);
}

.stage-dust::before {
	content: '';
	position: absolute;
	inset: 0;
	background: inherit;
	background-size: 150px 150px;
	opacity: .6;
	animation: stage-dust-near 10s linear infinite;
}

@keyframes stage-dust-far  { to { background-position: 50px -400px; } }
@keyframes stage-dust-near { to { background-position: -30px -300px; } }

.stage-spark {
	position: absolute;
	z-index: 3;
	background: rgb(255 250 235 / .85);
	border-radius: 50%;
	box-shadow: 0 0 5px 1px rgb(255 244 205 / .35);
	opacity: 0;
	pointer-events: none;
	will-change: transform, opacity;
	animation: stage-twinkle cubic-bezier(.35, 0, .5, 1) infinite;
}

@keyframes stage-twinkle {
	0%   { transform: translateY(0)      scale(.35); opacity: 0; }
	20%  { opacity: .28; }
	50%  { transform: translateY(-70px)  scale(1);   opacity: .34; }
	80%  { opacity: .12; }
	100% { transform: translateY(-150px) scale(.5);  opacity: 0; }
}

/*
 * The loading block: the ring, and under it what is being waited for.
 *
 * Centred as a column rather than the ring being centred on its own, because
 * the label has to sit with it and not under the frame — the caption that used
 * to live below the stage is what this replaces.
 */
.stage-loading {
	position: absolute;
	top: 50%;
	left: 50%;
	z-index: 4;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: .7rem;
	transform: translate(-50%, -50%);
	pointer-events: none;
}

.stage-spinner {
	width: 30px;
	height: 30px;
	border: 2px solid rgb(255 255 255 / .12);
	border-top-color: var(--accent);
	border-radius: 50%;
	animation: stage-spin .8s linear infinite;
}

.stage-loading-label {
	margin: 0;
	color: var(--ash);
	font-size: .72rem;
	letter-spacing: .18em;
	text-transform: uppercase;
	white-space: nowrap;
}

@keyframes stage-spin { to { transform: rotate(360deg); } }

/*
 * The mark that says the figure turns.
 *
 * Inside the frame, where the affordance is, rather than in a line of type
 * beneath it. It leaves on the first drag: an instruction that stays after it
 * has been followed is decoration.
 */
.stage-turn {
	position: absolute;
	top: .8rem;
	right: .9rem;
	z-index: 5;
	display: flex;
	color: var(--ash);
	opacity: .5;
	pointer-events: none;
	transition: opacity .45s ease;
	animation: stage-turn-hint 3.2s ease-in-out 1.2s 2;
}

.stage-turn.is-used {
	opacity: 0;
	animation: none;
}

@keyframes stage-turn-hint {
	0%, 100% { transform: rotate(0); }
	35%      { transform: rotate(-22deg); }
	65%      { transform: rotate(22deg); }
}

@media (prefers-reduced-motion: reduce) {
	.stage-turn { animation: none; }
}

/* ── Page ledes ──────────────────────────────────────────────── */

.lede {
	margin-bottom: 2.4rem;
	padding-bottom: 1.4rem;
	border-bottom: 1px solid var(--rule);
}

.lede-title {
	margin: 0;
	font-family: var(--display);
	font-weight: 700;
	font-size: clamp(2rem, 5vw, 3rem);
	line-height: 1;
	letter-spacing: -.02em;
}

.lede-note {
	margin: .8rem 0 0;
	max-width: 46rem;
	font-size: .9rem;
	line-height: 1.6;
	color: var(--ash);
}

.lede.is-wide .lede-note { max-width: 38rem; }

/* ── The crown ───────────────────────────────────────────────── */

.thrones {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 2.4rem;
	align-items: start;
}

@media (max-width: 62rem) { .thrones { grid-template-columns: 1fr; } }

.throne {
	padding: 1.6rem;
	background: var(--slab);
	border: 1px solid var(--rule-soft);
	border-top: 2px solid var(--rule);
}

.throne.nation-karus { border-top-color: var(--karus); }
.throne.nation-elmorad { border-top-color: var(--elmorad); }

.throne-nation {
	margin: 0;
	font-size: .72rem;
	letter-spacing: .24em;
	text-transform: uppercase;
	color: var(--ash);
}

.throne.nation-karus .throne-nation { color: var(--karus); }
.throne.nation-elmorad .throne-nation { color: var(--elmorad); }

.throne-king {
	display: block;
	margin: .4rem 0 0;
	font-family: var(--display);
	font-weight: 700;
	font-size: clamp(1.6rem, 3.4vw, 2.3rem);
	line-height: 1.05;
	color: var(--bone);
	text-decoration: none;
	overflow-wrap: anywhere;
}

.throne-king:hover { color: var(--accent); }
.throne-king.is-empty { color: var(--dim); }

.throne-stage { margin: .3rem 0 0; font-size: .76rem; color: var(--dim); }

.throne-figures {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 1rem;
	margin: 1.4rem 0 1.6rem;
	padding: 1rem 0;
	border-top: 1px solid var(--rule-soft);
	border-bottom: 1px solid var(--rule-soft);
}

.figure-value {
	margin: 0;
	font-family: var(--display);
	font-weight: 600;
	font-size: 1.05rem;
	font-variant-numeric: tabular-nums;
	color: var(--bone);
}

/*
 * The tariff, not the treasury, gets the large type.
 *
 * A treasury is a number about the king. A tariff is a number about everyone
 * else — it is taken from every trade in the nation — so it is the figure on
 * this page a reader is actually affected by.
 */
.figure.is-loud .figure-value { font-size: 2rem; line-height: 1; }
.figure.is-loud .figure-value span { font-size: .5em; margin-left: .05em; }
.throne.nation-karus .figure.is-loud .figure-value { color: var(--karus); }
.throne.nation-elmorad .figure.is-loud .figure-value { color: var(--elmorad); }

.figure-label {
	margin: .3rem 0 0;
	font-size: .68rem;
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var(--dim);
}

.throne-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	margin: 0 0 .4rem;
	font-size: .72rem;
	letter-spacing: .2em;
	text-transform: uppercase;
	color: var(--ash);
}

.throne-votes { letter-spacing: normal; text-transform: none; color: var(--dim); }

.entry.is-crowned .entry-name { color: var(--bone); }
.entry.is-flat { cursor: default; }

.ladder.is-tight .entry { padding: .55rem .7rem; gap: .8rem; }
.ladder.is-tight .entry { grid-template-columns: minmax(0, 1fr) auto; }
.ladder.is-tight .entry.is-clan,
.throne .ladder.is-tight .entry { grid-template-columns: 24px minmax(0, 1fr) auto; }
.throne .entry-mark { width: 24px; height: 24px; }

/* The crowns on the front page. */
.crowns {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 1.5rem;
	margin-top: 1.4rem;
}

.crown {
	display: flex;
	align-items: baseline;
	gap: .6rem;
	padding: .6rem .8rem;
	background: var(--slab);
	border-left: 2px solid var(--rule);
	text-decoration: none;
	min-width: 0;
}

.crown.nation-karus { border-left-color: var(--karus); }
.crown.nation-elmorad { border-left-color: var(--elmorad); }
.crowns > .crown:last-child:nth-child(2) { justify-content: flex-end; }

.crown-label {
	font-size: .66rem;
	letter-spacing: .18em;
	text-transform: uppercase;
	color: var(--dim);
}

.crown-name {
	font-weight: 600;
	color: var(--bone);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.crown:hover .crown-name { color: var(--accent); }
.crown-meta { margin-left: auto; font-size: .74rem; color: var(--ash); }

@media (max-width: 48rem) { .crowns { grid-template-columns: 1fr; } }

/*
 * ── The week ─────────────────────────────────────────────────────────────────
 *
 * Rebuilt from a seven-column timeline into seven rows, and the reason is the
 * whole design: a row is already the shape a phone wants, so there is no second
 * mobile layout to keep in step with the first. The old grid had absolutely
 * positioned cards inside 1/7th-width columns and nowhere to go under 40rem.
 *
 * Nothing here is fixed-height. A day with four wars is taller than a day with
 * one, which is honest and is also what makes the page compact — the old version
 * gave every day the same screen-height track whether it held four events or
 * none.
 */

/* ── What is next ─────────────────────────────────────────────── */

/*
 * The answer to why somebody opened this page, and the only place on it that
 * gets any size. The layout is a small stack: label, name, when, countdown, then
 * the three facts as a row that wraps.
 */
.upnext {
	margin: var(--s6) 0;
	padding: var(--s5);
	border: 1px solid var(--seam);
	border-radius: var(--round-card);
	/* The wash sits behind the top of the card and fades out, so the block reads
	   as lit from its own heading rather than as a flat tinted box. */
	background:
		linear-gradient(180deg, var(--gold-veil), transparent 60%),
		var(--plate);
	box-shadow: var(--lift), var(--drop);
	display: grid;
	gap: var(--s2);
}

/*
 * A siege is not a national war and the card says so without a badge.
 *
 * The accent shifts to the nations' contested colour — a siege is the one event
 * both sides fight over a single object — and it is the only tint on the page
 * that is not gold. One card, one deviation; a badge would have needed a legend.
 */
.upnext--siege {
	background:
		linear-gradient(180deg, color-mix(in oklab, var(--karus) 12%, transparent), transparent 60%),
		var(--plate);
}

.upnext-name {
	font-size: clamp(1.35rem, 3.4vw, 2rem);
	color: var(--parch);
}

.upnext-when {
	display: flex;
	align-items: baseline;
	gap: var(--s2);
	margin: 0;
}

.upnext-day {
	font-weight: 600;
	color: var(--parch-dim);
}

.upnext-clock {
	font-size: 1.15rem;
	font-weight: 700;
	color: var(--gold);
}

/* The countdown is the sentence a reader repeats to somebody else, so it is set
   at reading size and in the body face rather than as another number. */
.upnext-count {
	margin: 0;
	font-size: 1.05rem;
	color: var(--parch);
}

.upnext-facts {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s2) var(--s5);
	margin: var(--s3) 0 0;
	padding-top: var(--s3);
	border-top: 1px solid var(--seam-soft);
}

.upnext-facts div { display: grid; gap: 2px; }

.upnext-facts dt {
	font: 600 .68rem/1 var(--ui);
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--parch-faint);
}

.upnext-facts dd {
	margin: 0;
	font-size: .95rem;
	color: var(--parch);
}

/* ── The seven days ───────────────────────────────────────────── */

.week { margin: var(--s7) 0 var(--s8); }

.week-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--s4);
	padding-bottom: var(--s3);
	border-bottom: 1px solid var(--seam);
}

.week-title { font-size: 1.1rem; }

.week-summary {
	margin: 0;
	font-size: .85rem;
	color: var(--parch-faint);
}

.week-days {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	/* A hairline between days instead of a gap and a border each: seven cards with
	   their own outlines read as seven unrelated things, and this is one week. */
	gap: 0;
}

.day {
	display: grid;
	/* Day name in a fixed column, everything else in the rest — so the seven names
	   line up down the page and the eye can find Saturday without reading. The
	   name column is sized in `ch` off the longest Turkish weekday. */
	grid-template-columns: 10ch 1fr;
	gap: var(--s2) var(--s4);
	align-items: start;
	padding: var(--s4) 0;
	border-bottom: 1px solid var(--seam-soft);
}

.day-name {
	grid-column: 1;
	grid-row: 1 / span 2;
	display: flex;
	flex-direction: column;
	gap: 2px;
	font-size: .95rem;
	color: var(--parch-dim);
}

.day.is-today .day-name { color: var(--gold); }

.day-today {
	font: 600 .62rem/1 var(--ui);
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--gold-ink);
	background: var(--gold);
	border-radius: var(--round-pill);
	padding: 3px 7px;
	width: fit-content;
}

/* A quiet day recedes rather than disappearing: the reader is looking for the
   gaps as much as for the wars, so an empty row still has to be a row. */
.day.is-quiet { opacity: .55; }

/*
 * ── The day's shape ──────────────────────────────────────────────────────────
 *
 * A 24-hour track, 6px tall. This is what the old screen-height timeline was
 * for — seeing at a glance that Saturday is an evening and Monday runs all day —
 * and as a row it costs six pixels instead of a viewport.
 *
 * Position and width both carry data: `--at` is the start as a share of the day,
 * `--wide` is the duration on the same scale. A two-hour war is visibly twice a
 * one-hour siege, which no chip can say.
 */
.day-track {
	grid-column: 2;
	position: relative;
	height: 6px;
	border-radius: var(--round-pill);
	background: var(--seam-soft);
	overflow: hidden;
}

.day-mark {
	position: absolute;
	top: 0;
	bottom: 0;
	left: var(--at);
	/* A floor on the width, so a short event is still a visible mark rather than a
	   hairline nobody notices. */
	width: max(3px, var(--wide));
	border-radius: var(--round-pill);
	background: var(--gold);
}

.day-mark--siege { background: var(--karus); }

.day-none {
	grid-column: 2;
	margin: 0;
	font-size: .85rem;
	color: var(--parch-faint);
}

/* ── The day's events ─────────────────────────────────────────── */

/*
 * Chips that wrap. On a wide screen a four-war Monday is one line; on a phone it
 * is four. Both are the same markup and neither has a breakpoint, because the
 * flow already does the work.
 */
.day-slots {
	grid-column: 2;
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: var(--s2);
}

.slot {
	display: flex;
	align-items: baseline;
	gap: var(--s2);
	padding: var(--s2) var(--s3);
	border: 1px solid var(--seam);
	/* Square, not rounded: this is data. See the radius note in the tokens. */
	border-radius: var(--round-data);
	background: var(--plate);
	/* The kind as a left edge rather than a badge — it needs no legend, because
	   the track above uses the same two colours. */
	border-left: 2px solid var(--gold);
}

.slot--siege { border-left-color: var(--karus); }

.slot-time {
	font-size: .95rem;
	font-weight: 700;
	color: var(--parch);
}

.slot-body { display: grid; gap: 1px; }

.slot-name {
	font-size: .85rem;
	font-weight: 600;
	color: var(--parch-dim);
}

.slot-meta {
	font-size: .76rem;
	color: var(--parch-faint);
	display: flex;
	flex-wrap: wrap;
	gap: 0 .35em;
	align-items: baseline;
}

/*
 * The zone, demoted to what it is.
 *
 * `BattleZone_e` is the server's own identifier, not a place a visitor could
 * recognise, and the page used to print it as though it were a name. Inventing a
 * friendly label would be inventing data, so instead it is set small and in mono
 * — which reads as "technical detail" to everyone and stays useful to the players
 * who know these strings.
 */
.slot-zone {
	font-size: .7rem;
	color: var(--parch-faint);
	opacity: .8;
}

/*
 * On a phone the name stops being a column and becomes a heading above its day.
 * A 10ch column plus wrapped chips at 360px leaves the chips about twelve
 * characters of room, which is narrower than one of them.
 */
@media (max-width: 34rem) {
	.day {
		grid-template-columns: 1fr;
		gap: var(--s2);
	}

	.day-name {
		grid-row: auto;
		flex-direction: row;
		align-items: center;
		gap: var(--s2);
	}

	.day-track, .day-slots, .day-none { grid-column: 1; }

	.upnext-facts { gap: var(--s2) var(--s4); }
}

/* ── The census ──────────────────────────────────────────────── */

.curve-block { margin-bottom: 2.8rem; }

.curve {
	display: flex;
	align-items: flex-end;
	gap: 1px;
	height: 13rem;
	margin-top: 1.2rem;
	padding-top: 1rem;
}

.curve-bar {
	flex: 1;
	min-width: 2px;
	height: calc(var(--h) * 100%);
	background: var(--rule);
	transition: background .15s;
}

.curve-bar:hover { background: var(--bone); }
.curve-bar.is-peak { background: var(--accent); }

.curve-axis {
	display: flex;
	justify-content: space-between;
	margin-top: .5rem;
	padding-top: .5rem;
	border-top: 1px solid var(--rule-soft);
	font-size: .68rem;
	color: var(--dim);
}

.curve-note { margin: .8rem 0 0; font-size: .72rem; color: var(--dim); }

.races {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
	gap: 1px;
	background: var(--rule-soft);
	border: 1px solid var(--rule-soft);
	margin-top: .3rem;
}

.race { padding: 1.1rem 1rem; background: var(--ink); }

.race-share {
	margin: 0;
	font-family: var(--display);
	font-weight: 700;
	font-size: 1.6rem;
	line-height: 1;
	font-variant-numeric: tabular-nums;
	color: var(--ash);
}

.race-share span { font-size: .5em; margin-left: .05em; }
.race.nation-karus .race-share { color: var(--karus); }
.race.nation-elmorad .race-share { color: var(--elmorad); }

.race-name { margin: .45rem 0 0; font-size: .84rem; font-weight: 500; color: var(--bone); }
.race-count { margin: .1rem 0 0; font-size: .7rem; color: var(--dim); }

/* ── News ────────────────────────────────────────────────────── */

.strip { margin: 2.8rem 0; }

.strip-row {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
	gap: 1px;
	background: var(--rule-soft);
	border: 1px solid var(--rule-soft);
	border-top: 0;
}

.brief {
	display: flex;
	flex-direction: column;
	gap: .3rem;
	padding: .9rem 1rem;
	background: var(--ink);
	text-decoration: none;
}

.brief:hover { background: var(--slab); }

.brief-kicker {
	font-size: .64rem;
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var(--accent);
}

.brief-title {
	font-size: .88rem;
	font-weight: 500;
	line-height: 1.35;
	color: var(--bone);
}

.brief-date { margin-top: auto; font-size: .68rem; color: var(--dim); }

.lead {
	padding-bottom: 2rem;
	margin-bottom: 2rem;
	border-bottom: 1px solid var(--rule);
}

.lead-kicker,
.article-kicker,
.post-kicker {
	display: flex;
	align-items: center;
	gap: .7rem;
	margin: 0 0 .9rem;
}

.lead-date,
.post-date,
.article-date { font-size: .72rem; color: var(--dim); }

.lead-title {
	margin: 0;
	font-family: var(--display);
	font-weight: 700;
	font-size: clamp(2rem, 5.5vw, 3.4rem);
	line-height: 1.02;
	letter-spacing: -.02em;
	max-width: 22ch;
}

.lead-title a { text-decoration: none; }
.lead-title a:hover { color: var(--accent); }

.lead-excerpt {
	margin: 1rem 0 0;
	max-width: 48rem;
	font-size: 1rem;
	line-height: 1.65;
	color: var(--ash);
}

.lead-by,
.article-by { margin: 1rem 0 0; font-size: .74rem; color: var(--dim); }

.posts {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
	gap: 1px;
	background: var(--rule-soft);
	border: 1px solid var(--rule-soft);
}

.post { background: var(--ink); }

.post-link {
	display: block;
	height: 100%;
	padding: 1.3rem 1.4rem;
	text-decoration: none;
}

.post-link:hover { background: var(--slab); }

.post-title {
	margin: 0;
	font-family: var(--display);
	font-size: 1.15rem;
	font-weight: 600;
	line-height: 1.25;
	color: var(--bone);
}

.post-excerpt {
	margin: .6rem 0 0;
	font-size: .82rem;
	line-height: 1.55;
	color: var(--ash);
}

.post-by { margin: .8rem 0 0; font-size: .7rem; color: var(--dim); }

.article { max-width: 44rem; margin-bottom: 3rem; }
.article-back { font-size: .72rem; color: var(--ash); text-decoration: none; }
.article-back:hover { color: var(--accent); }

.article-title {
	margin: 0;
	font-family: var(--display);
	font-weight: 700;
	font-size: clamp(1.9rem, 4.5vw, 2.8rem);
	line-height: 1.06;
	letter-spacing: -.02em;
}

.prose {
	margin-top: 1.8rem;
	font-size: .95rem;
	line-height: 1.75;
	color: #d8d8d6;
}

.prose p { margin: 0 0 1.1rem; }
.prose strong { color: var(--bone); font-weight: 600; }
.prose ul { margin: 0 0 1.1rem; padding-left: 1.1rem; list-style: none; }

.prose li { position: relative; margin-bottom: .35rem; }

.prose li::before {
	content: '';
	position: absolute;
	left: -1.1rem;
	top: .65em;
	width: 5px;
	height: 1px;
	background: var(--accent);
}

/* ── Download ────────────────────────────────────────────────── */

.grab { margin-bottom: 3rem; }

.mirrors { display: flex; flex-wrap: wrap; gap: .8rem; }

.mirror {
	display: flex;
	flex-direction: column;
	gap: .15rem;
	padding: .9rem 1.6rem;
	border: 1px solid var(--rule);
	text-decoration: none;
	color: var(--bone);
}

.mirror:hover { border-color: var(--accent); }

.mirror.is-primary {
	background: var(--accent);
	border-color: var(--accent);
	color: var(--ink);
}

.mirror.is-primary:hover { background: var(--accent-soft); }

.mirror-name {
	font-size: .82rem;
	font-weight: 700;
	letter-spacing: .12em;
	text-transform: uppercase;
}

.mirror-note { font-size: .7rem; opacity: .8; }

.grab-facts { display: flex; flex-wrap: wrap; gap: 1.4rem; margin: 1.2rem 0 0; font-size: .78rem; color: var(--dim); }
.grab-facts strong { color: var(--bone); font-weight: 600; }
.grab-patch { margin: .8rem 0 0; font-size: .8rem; color: var(--ash); }

.notice-title { margin: 0 0 .4rem; font-weight: 600; color: var(--bone); }
.notice-body { margin: 0; font-size: .86rem; line-height: 1.6; color: var(--ash); max-width: 44rem; }

.notice code,
.step-body code {
	padding: .05rem .3rem;
	background: rgb(255 255 255 / .06);
	color: var(--bone);
	font-size: .9em;
}

/*
 * Numbered, and the numbers are earned: this is a real sequence — step three
 * cannot happen before step two. If the items could be shuffled without harm
 * they would not carry numbers.
 */
.steps {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
	gap: 1px;
	background: var(--rule-soft);
	border: 1px solid var(--rule-soft);
	margin-bottom: 3rem;
}

.step { padding: 1.6rem 1.5rem; background: var(--ink); }

.step-no {
	margin: 0 0 .8rem;
	font-family: var(--display);
	font-weight: 700;
	font-size: 2.2rem;
	line-height: 1;
	color: var(--rule);
}

.step-title { margin: 0; font-size: 1rem; font-weight: 600; color: var(--bone); }
.step-body { margin: .6rem 0 0; font-size: .84rem; line-height: 1.6; color: var(--ash); }

.specs-title {
	margin: 0 0 1rem;
	font-size: .76rem;
	letter-spacing: .22em;
	text-transform: uppercase;
	color: var(--ash);
}

.specs-list { margin: 0; display: grid; gap: 1px; background: var(--rule-soft); border: 1px solid var(--rule-soft); }

.spec {
	display: flex;
	justify-content: space-between;
	gap: 1rem;
	padding: .7rem 1rem;
	background: var(--ink);
	font-size: .82rem;
}

.spec dt { color: var(--dim); }
.spec dd { margin: 0; color: var(--bone); text-align: right; }

/* ── Help ────────────────────────────────────────────────────── */

.channels { display: flex; flex-wrap: wrap; gap: 1px; background: var(--rule-soft); border: 1px solid var(--rule-soft); margin-bottom: 2.4rem; }

.channel {
	flex: 1 1 12rem;
	padding: 1rem 1.2rem;
	background: var(--ink);
	text-decoration: none;
}

a.channel:hover { background: var(--slab); }

.channel-label {
	display: block;
	font-size: .66rem;
	letter-spacing: .18em;
	text-transform: uppercase;
	color: var(--dim);
}

.channel-value { display: block; margin-top: .2rem; font-size: .86rem; color: var(--bone); overflow-wrap: anywhere; }

.faq { max-width: 46rem; }

.ask { border-bottom: 1px solid var(--rule-soft); }

.ask-q {
	padding: 1rem .2rem;
	font-size: .94rem;
	font-weight: 500;
	color: var(--bone);
	cursor: pointer;
	list-style: none;
}

.ask-q::-webkit-details-marker { display: none; }

.ask-q::before {
	content: '+';
	display: inline-block;
	width: 1.2rem;
	color: var(--accent);
	font-weight: 400;
}

.ask[open] .ask-q::before { content: '−'; }
.ask-q:hover { color: var(--accent); }

.ask-a {
	margin: 0 0 1.1rem 1.2rem;
	font-size: .86rem;
	line-height: 1.7;
	color: var(--ash);
}

/* ── Footer ──────────────────────────────────────────────────── */

.footer-grid {
	display: grid;
	grid-template-columns: minmax(0, 2fr) repeat(3, minmax(0, 1fr));
	gap: 2rem;
	max-width: var(--measure);
	margin: 0 auto;
	padding: 0 var(--gutter);
}

@media (max-width: 52rem) { .footer-grid { grid-template-columns: 1fr 1fr; } }

.footer-brand {
	margin: 0;
	font-family: var(--display);
	font-weight: 600;
	letter-spacing: .18em;
	text-transform: uppercase;
	color: var(--bone);
}

.footer-note { margin: .5rem 0 0; font-size: .76rem; line-height: 1.6; color: var(--dim); max-width: 24rem; }

.footer-links { display: flex; flex-direction: column; gap: .35rem; }

.footer-head {
	margin: 0 0 .3rem;
	font-size: .64rem;
	letter-spacing: .2em;
	text-transform: uppercase;
	color: var(--dim);
}

.footer-links a { font-size: .8rem; color: var(--ash); text-decoration: none; }
.footer-links a:hover { color: var(--accent); }

/* ── Arrival ─────────────────────────────────────────────────── */

/*
 * One orchestrated entrance and nothing else. The sheet's sections come up in
 * order, a beat apart, which reads as a page assembling itself rather than as
 * five separate effects.
 */
.sheet > * {
	animation: rise .5s cubic-bezier(.2, .7, .3, 1) backwards;
}

.sheet > :nth-child(1) { animation-delay: .04s; }
.sheet > :nth-child(2) { animation-delay: .10s; }
.sheet > :nth-child(3) { animation-delay: .16s; }
.sheet > :nth-child(4) { animation-delay: .22s; }
.sheet > :nth-child(5) { animation-delay: .28s; }

@keyframes rise {
	from { opacity: 0; transform: translateY(8px); }
	to   { opacity: 1; transform: none; }
}

:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
	.stage-ray,
	.stage-dust,
	.stage-dust::before,
	.stage-spark { animation: none; }

	.stage-spark { opacity: .5; }
	.sheet > * { animation: none; }
}

/* ── The podium ──────────────────────────────────────────────── */

/*
 * Three characters at one size, in rank order.
 *
 * Scaling the winner up reads as a podium and ruins the comparison — three
 * figures drawn at three sizes cannot be measured against each other, which is
 * the only reason to stand them side by side. The placing is carried twice
 * instead, by the tab over the card and by the numeral behind the figure, and
 * neither of those touches the thing being compared.
 */
/*
 * The podium and the ladder are one page, not two.
 *
 * Three rem under the cards pushed the first ordinary row below the fold on a
 * laptop, which read as an elite page that happens to have a list somewhere
 * underneath. They are the same board: the three at the top and everyone behind
 * them, and the gap should say "these three" rather than "a different section".
 */
.podium {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 1rem;
	margin-bottom: 1.4rem;
}

@media (max-width: 64rem) { .podium { grid-template-columns: 1fr; } }

.pedestal {
	--place: var(--rule);

	position: relative;
	padding-top: .8rem;
	min-width: 0;
}

/* Gold, silver, bronze — the one place on this site where the medal colours are
   the right answer, because the page is a placing and nothing else. */
.pedestal.is-rank-1 { --place: #e3b341; }
.pedestal.is-rank-2 { --place: #c9ccd1; }
.pedestal.is-rank-3 { --place: #c07a3e; }

/* The tab sits *on* the card's top edge, the way the reference marks its three. */
.pedestal-tab {
	position: absolute;
	top: 0;
	left: 50%;
	z-index: 2;
	transform: translateX(-50%);
	margin: 0;
	padding: .2rem .8rem;
	background: var(--ink);
	border: 1px solid var(--place);
	border-radius: var(--round);
	color: var(--place);
	font-size: .64rem;
	font-weight: 600;
	letter-spacing: .2em;
	text-transform: uppercase;
	white-space: nowrap;
}

.pedestal-body {
	display: block;
	position: relative;
	border: 1px solid var(--place);
	border-radius: var(--round);
	background: linear-gradient(180deg, rgb(255 255 255 / .02), transparent 40%);
	text-decoration: none;
	overflow: hidden;
}

/*
 * Hover lights the name, not the plate.
 *
 * A white wash over a scrim that is already a gradient reads as a smear; the
 * name's own colour change (below) is the whole affordance a card this size
 * needs.
 */

.pedestal-rank {
	position: absolute;
	top: .6rem;
	left: .9rem;
	z-index: 1;
	font-family: var(--display);
	font-weight: 700;
	font-size: 3.4rem;
	line-height: 1;
	color: rgb(255 255 255 / .07);
	pointer-events: none;
}

/*
 * No painted room behind these.
 *
 * The character page's set is a room, and repeating it three times puts three
 * different people in three copies of the same place — which reads as wallpaper
 * rather than as a comparison. Stripped back, the figures are the only thing on
 * the card, which is the point of the page.
 */
.pedestal-stage {
	display: block;
	position: relative;
	aspect-ratio: 4 / 5;
	/*
	 * No pool of light under the feet either.
	 *
	 * A faint ellipse sat here as a floor for the figure to stand on. Together with
	 * the viewer's painted ground shadow — now off, see `Viewer` — it made two round
	 * marks under the boots, one dark and one light, on a card that has no floor at
	 * all. The card's own bevel is what says the figure is standing in something.
	 */
	background: none;
}

/* A second line of defence: even out of flow, nothing escapes the frame. */
.pedestal-stage { overflow: hidden; }

/*
 * ── The nameplate, over the figure rather than under it ─────────────────────
 *
 * It used to be a band below the stage, and the stage clipped to its own box —
 * so a character's glow ran down to the plate's top edge and **stopped**, on a
 * hard horizontal line the effect never has in game. Auras reach past the boots;
 * a card that ends the figure's frame partway through one cuts it in half.
 *
 * Laid over the bottom of the stage instead, on a scrim rather than a panel. The
 * effect now runs behind the name to the card's own edge, and the gradient — not
 * a flat tint — is what keeps the name readable over a figure that may be white
 * at the ankles and black at the hem. A single translucent fill cannot do both:
 * light enough to see the glow through and it stops holding the text.
 */
.pedestal-plate {
	display: block;
	position: absolute;
	inset: auto 0 0 0;
	z-index: 20;
	padding: 2.4rem 1rem 1rem;
	text-decoration: none;
	/*
	 * **Fully transparent.** No scrim either — the figure runs all the way to the
	 * card's edge and the plate contributes nothing but text.
	 *
	 * That leaves legibility to the type instead of to a panel behind it, which is
	 * what `text-shadow` on the name and the meta line below is for: a dark halo
	 * travels with the glyphs, so it holds over a white boot and costs nothing over
	 * empty space. A flat tint would have had to be dark enough for the brightest
	 * pixel it might ever cover, and that is a panel again.
	 */
	background: none;
	/*
	 * No seam at all. A hairline across the top is the border-top by another
	 * name — it draws the very line the scrim exists to dissolve, and with the
	 * effect running behind it the join has nothing to hide.
	 */
}

.pedestal-name {
	display: block;
	/* Its own backdrop, so the plate needs none. */
	text-shadow: 0 1px 3px rgb(0 0 0 / .95), 0 0 12px rgb(0 0 0 / .8);
	font-family: var(--display);
	font-weight: 700;
	font-size: 1.2rem;
	color: var(--bone);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.pedestal-plate:hover .pedestal-name { color: var(--place); }

.pedestal-meta {
	display: block;
	margin-top: .2rem;
	font-size: .74rem;
	color: var(--ash);
	text-shadow: 0 1px 3px rgb(0 0 0 / .95), 0 0 8px rgb(0 0 0 / .85);
}

.pedestal-value {
	display: block;
	margin-top: .6rem;
	font-size: 1.15rem;
	font-weight: 600;
	color: var(--place);
	font-variant-numeric: tabular-nums;
	/* The number is the brightest thing on the card and needs the halo most. */
	text-shadow: 0 1px 3px rgb(0 0 0 / .95), 0 0 10px rgb(0 0 0 / .9);
}

/* ── Corners ─────────────────────────────────────────────────── */

/*
 * One radius, applied to the things that are boxes.
 *
 * Kept as a single list rather than scattered through the sheet so the softness
 * of the whole panel is one edit. Ladder rows are left square on purpose: they
 * sit edge to edge in a stack, and rounding them would break the column of bars
 * into a strip of separate pills — the bars have to read as one measure.
 */
.throne,
.slot,
.crown,
.notice,
.mirror,
.step,
.spec,
.race,
.brief,
.post,
.channel,
.ask,
.stage,
.week,
.tag,
.entry-badge,
.find-input,
.play,
.menu-panel,
.strip-row,
.posts,
.steps,
.specs-list,
.races,
.channels { border-radius: var(--round); }

/* Cells in a seamless grid: round the grid, not every tile inside it. */
.strip-row,
.posts,
.steps,
.specs-list,
.races,
.channels { overflow: hidden; }

.brief,
.post,
.step,
.spec,
.race,
.channel { border-radius: 0; }

/* ── Rebirth ─────────────────────────────────────────────────── */

/*
 * The rebirth bonus rides beside the stat it was spent on, not folded into it.
 *
 * A rebirth grants two points per level and the game keeps them in their own
 * columns — the base is identical for every character at the cap. Summing them
 * would report a number the game never shows and lose the only part of the block
 * the player chose a second time.
 */
.stat-bonus {
	margin-left: .25rem;
	font-size: .62em;
	font-weight: 600;
	color: var(--good);
	vertical-align: .35em;
}

.stat.has-rebirth dd { color: var(--bone); }

.rebirth {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: .6rem;
	margin: .9rem 0 0;
	padding-top: .8rem;
	border-top: 1px solid var(--rule-soft);
	font-size: .78rem;
	color: var(--ash);
}

.rebirth-mark {
	padding: .12rem .45rem;
	border: 1px solid var(--good);
	border-radius: var(--round);
	color: var(--good);
	letter-spacing: .1em;
	text-transform: uppercase;
	font-size: .68rem;
}

.rebirth-mark strong { font-weight: 700; }
.rebirth-note { color: var(--dim); }

/* ── Vitals and record ───────────────────────────────────────── */

/*
 * Health and mana share one row, and their widths are their share of the pair.
 *
 * Two bars each drawn against their own maximum say nothing side by side — every
 * character would read as full. Drawn against each other they say what class the
 * character actually is: a mage's row is the mirror of a warrior's.
 */
.vitals {
	display: flex;
	gap: 2px;
	margin-top: 1rem;
}

.vital {
	position: relative;
	flex: var(--fill, .5) 1 0;
	min-width: 5.5rem;
	padding: .55rem .7rem;
	background: rgb(255 255 255 / .03);
	border-radius: var(--round);
	overflow: hidden;
	isolation: isolate;
}

.vital::before {
	content: '';
	position: absolute;
	inset: 0;
	z-index: -1;
	background: currentColor;
	opacity: .16;
}

.vital.is-hp { color: var(--karus); }
.vital.is-mp { color: var(--elmorad); }

.vital-label {
	display: block;
	font-size: .62rem;
	letter-spacing: .2em;
	color: var(--ash);
}

.vital-value {
	display: block;
	margin-top: .1rem;
	font-size: 1rem;
	font-weight: 600;
	color: var(--bone);
	font-variant-numeric: tabular-nums;
}

.record {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
	gap: 1px;
	background: var(--rule-soft);
	border: 1px solid var(--rule-soft);
	border-radius: var(--round);
	overflow: hidden;
}

.record-cell { padding: .8rem .9rem; background: var(--ink); }

.record-value {
	margin: 0;
	font-family: var(--display);
	font-weight: 600;
	font-size: 1.15rem;
	line-height: 1;
	color: var(--bone);
	font-variant-numeric: tabular-nums;
}

.record-label {
	margin: .35rem 0 0;
	font-size: .64rem;
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var(--dim);
}

.record-note { margin: .2rem 0 0; font-size: .68rem; color: var(--ash); }

.record-ranks { margin: .9rem 0 0; font-size: .8rem; color: var(--ash); }
.record-ranks strong { color: var(--bone); }

/* The set block on an item card — the game's own colours: a yellow heading over
   green bonuses, so a suit's contribution reads apart from the item's own. */
.ic-set-name {
	margin: 0 0 .2rem;
	color: #cdc300;
	font-weight: 600;
}

.ic-line.is-set { color: #80ff00; }

/* ── Skill build ─────────────────────────────────────────────── */

/*
 * Four masteries, drawn against each other rather than against a cap.
 *
 * Every character at the level cap has the same 148 points to spend, so the
 * question the block answers is not "how many" but "where" — and a bar measured
 * against the tallest of the four answers that at a glance: one tall bar is a
 * specialist, two is a hybrid.
 */
.trees {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: .5rem;
	align-items: end;
	height: 5rem;
}

.tree {
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	align-items: center;
	gap: .3rem;
	height: 100%;
}

.tree-bar {
	display: block;
	width: 100%;
	height: calc(var(--fill) * 100%);
	min-height: 2px;
	background: var(--rule);
	border-radius: var(--round) var(--round) 0 0;
}

.tree.is-main .tree-bar { background: var(--accent); }

.tree-value {
	font-size: .78rem;
	font-variant-numeric: tabular-nums;
	color: var(--ash);
}

.tree.is-main .tree-value { color: var(--bone); font-weight: 600; }

.trees-note { margin: .7rem 0 0; font-size: .74rem; color: var(--dim); }

/* ── Paging ──────────────────────────────────────────────────── */

.pager {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: .3rem;
	margin-top: 1.6rem;
	padding-top: 1.2rem;
	border-top: 1px solid var(--rule-soft);
	font-size: .8rem;
}

.pager-page,
.pager-step {
	padding: .35rem .7rem;
	border: 1px solid var(--rule-soft);
	border-radius: var(--round);
	color: var(--ash);
	text-decoration: none;
	font-variant-numeric: tabular-nums;
}

.pager-page:hover,
.pager-step:hover { border-color: var(--rule); color: var(--bone); }

.pager-page.is-here {
	background: var(--accent);
	border-color: var(--accent);
	color: var(--ink);
	font-weight: 600;
}

.pager-step { margin-inline: .4rem; }
.pager-gap { padding: 0 .2rem; color: var(--dim); }

/* ── The next war ────────────────────────────────────────────── */

/*
 * One line, and the only thing on the front page with a clock in it.
 *
 * A player who already plays opens this page to learn one thing — when the next
 * war is — and the schedule repeats weekly, which nobody memorises. It sits
 * between the standing and the podium because it is the page's one call to act.
 */
.callout {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 1rem;
	margin: 2.4rem 0;
	padding: .9rem 1.2rem;
	background: var(--slab);
	border: 1px solid var(--rule-soft);
	border-left: 2px solid var(--accent);
	border-radius: var(--round);
	text-decoration: none;
}

.callout:hover { border-color: var(--rule); border-left-color: var(--accent); }

.callout-label {
	font-size: .64rem;
	letter-spacing: .2em;
	text-transform: uppercase;
	color: var(--dim);
}

.callout-main { display: flex; align-items: baseline; gap: .6rem; min-width: 0; }
.callout-main strong { color: var(--bone); font-weight: 600; }
.callout-where { font-size: .8rem; color: var(--ash); }

.callout-when {
	display: flex;
	align-items: center;
	gap: .6rem;
	margin-left: auto;
	font-size: .82rem;
	color: var(--accent);
	font-variant-numeric: tabular-nums;
}

.callout-tag {
	padding: .1rem .4rem;
	border: 1px solid var(--rule-soft);
	border-radius: var(--round);
	font-size: .66rem;
	color: var(--ash);
}

/* ── News slider ─────────────────────────────────────────────── */

/*
 * A rail that snaps, built on the browser's own scrolling.
 *
 * `scroll-snap-type` plus `scrollBy` is the whole mechanism — no library, and it
 * degrades to a perfectly usable scrollable row if the script never runs. The
 * arrows move by one card's width measured from the DOM, so the step survives
 * every breakpoint without a second set of numbers.
 */
.slider { margin: 2.8rem 0; }

.slider-nav { display: flex; align-items: center; gap: .4rem; }

.slider-arrow {
	width: 1.9rem;
	height: 1.9rem;
	padding: 0;
	background: transparent;
	border: 1px solid var(--rule-soft);
	border-radius: var(--round);
	color: var(--ash);
	font: 400 .9rem var(--ui);
	cursor: pointer;
}

.slider-arrow:hover { border-color: var(--accent); color: var(--accent); }

.slider-rail {
	display: grid;
	grid-auto-flow: column;
	grid-auto-columns: minmax(16rem, 22rem);
	gap: 1rem;
	margin-top: .8rem;
	padding-bottom: .6rem;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scrollbar-width: thin;
	scrollbar-color: var(--rule) transparent;
}

.slide {
	display: flex;
	flex-direction: column;
	gap: .4rem;
	padding: 1.1rem 1.2rem;
	background: var(--slab);
	border: 1px solid var(--rule-soft);
	border-radius: var(--round);
	text-decoration: none;
	scroll-snap-align: start;
}

.slide:hover { border-color: var(--rule); }

.slide-kicker {
	font-size: .64rem;
	letter-spacing: .18em;
	text-transform: uppercase;
	color: var(--accent);
}

.slide-title {
	font-family: var(--display);
	font-size: 1.05rem;
	font-weight: 600;
	line-height: 1.3;
	color: var(--bone);
}

.slide-excerpt {
	font-size: .8rem;
	line-height: 1.55;
	color: var(--ash);
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.slide-foot { margin-top: auto; padding-top: .4rem; font-size: .7rem; color: var(--dim); }

/* ── Hero ────────────────────────────────────────────────────── */

/*
 * Full-bleed, and the only element on the site that breaks the measure.
 *
 * `100vw` with a negative margin rather than a wrapper: the page is one centred
 * column and adding a container around every *other* block, just so this one can
 * escape, would be the tail wagging the dog.
 */
.hero {
	position: relative;
	width: 100vw;
	margin-left: calc(50% - 50vw);
	margin-top: -2.2rem;
	margin-bottom: 2.6rem;
	height: clamp(20rem, 42vw, 30rem);
	/*
	 * The page's own colour, not `#000`.
	 *
	 * Pure black was invisible while every slide carried a full-bleed picture. The
	 * champion slide has no picture — the figure stands on the frame itself — and
	 * against the warm near-black page a `#000` hero reads as a black box sitting
	 * on it. Same token as the body, so the frame and the page are one surface and
	 * only the character is on it.
	 */
	background: var(--void);
}

/*
 * The copy sits in the middle of the frame, not on its floor.
 *
 * Bottom-aligned it reads as a caption under a picture; centred it reads as the
 * picture's subject, which is what a hero is. The scrim below still darkens the
 * lower half, so the text keeps its contrast either way.
 */
.hero-slide {
	position: relative;
	display: flex;
	align-items: center;
	overflow: hidden;
}

/*
 * The backdrop is the client's own scene art, darkened towards the bottom-left
 * so the headline has somewhere to sit. A flat scrim over the whole picture
 * would dim the art; a corner gradient keeps it bright where nothing is written.
 */
.hero-slide::before {
	content: '';
	position: absolute;
	inset: 0;
	background: var(--shot) center / cover no-repeat;
}

.hero-slide::after {
	content: '';
	position: absolute;
	inset: 0;
	background:
		linear-gradient(90deg, rgb(8 8 10 / .92) 0%, rgb(8 8 10 / .72) 45%, transparent 80%),
		linear-gradient(0deg, rgb(8 8 10 / .95) 0%, transparent 55%);
}

.hero-body {
	position: relative;
	z-index: 1;
	max-width: var(--measure);
	width: 100%;
	margin: 0 auto;
	padding: 0 var(--gutter);
}

.hero-kicker {
	margin: 0 0 .6rem;
	font-size: .68rem;
	letter-spacing: .24em;
	text-transform: uppercase;
	color: var(--accent);
}

.hero-title {
	margin: 0;
	max-width: 20ch;
	font-family: var(--display);
	font-weight: 700;
	font-size: clamp(1.9rem, 4.4vw, 3.2rem);
	line-height: 1.04;
	letter-spacing: -.02em;
	color: var(--bone);
}

.hero-lead {
	margin: .8rem 0 0;
	max-width: 46ch;
	font-size: .92rem;
	line-height: 1.6;
	color: var(--ash);
}

.hero-acts { display: flex; flex-wrap: wrap; gap: .6rem; margin: 1.4rem 0 0; }

.hero-go,
.hero-alt {
	padding: .6rem 1.3rem;
	border-radius: var(--round);
	font-size: .76rem;
	font-weight: 600;
	letter-spacing: .12em;
	text-transform: uppercase;
	text-decoration: none;
}

.hero-go { background: var(--accent); color: var(--ink); }
.hero-go:hover { background: var(--accent-soft); }
.hero-alt { border: 1px solid var(--rule); color: var(--bone); }
.hero-alt:hover { border-color: var(--accent); color: var(--accent); }

/* Swiper's own furniture, brought into the palette. */
/* ── Hero: video and the live figure ─────────────────────────── */

/*
 * The video, behind everything and under the same scrim as an image.
 *
 * `object-fit: cover` and full-bleed positioning rather than a background: a
 * `<video>` cannot be a CSS background, and it has to behave exactly as the
 * poster image does or the slider would jump between two different crops as it
 * pages from a video slide to a still one.
 *
 * It sits *under* `::after`, which is the scrim, so the headline keeps its
 * contrast over footage whose brightness nobody controls. That matters more with
 * video than with a still: a still can be chosen for a dark left side, a clip
 * cannot.
 */
.hero-video {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	/* Above the `::before` still, below the `::after` scrim. */
	z-index: 0;
	/* Nothing to interact with — the slider owns the pointer here. */
	pointer-events: none;
}

.hero-slide::after { z-index: 1; }
.hero-body { z-index: 2; }

/*
 * ── The champion's stage ─────────────────────────────────────────────────────
 *
 * The top-ranked character, live, standing in the right-hand third.
 *
 * A promotional still is what every site for this game opens with; this is a row
 * out of the database wearing what it is actually wearing, which is the one thing
 * none of them can do. So it gets the position a hero image would normally get —
 * the side the eye lands on after the headline — and the text keeps the left.
 *
 * Right-hand *third*, not half: the figure is tall and narrow, and a wide box
 * around it is mostly empty stage. The canvas is transparent (`backdrop: false`)
 * so whatever the slide's own background is shows through behind the character.
 */
.hero-stage {
	position: absolute;
	z-index: 2;
	top: 0;
	bottom: 0;
	/*
	 * The figure's right edge lands on the page's right margin, not the window's.
	 *
	 * The hero is the one full-bleed element on the site, so `right: 0` put the
	 * character against the edge of the screen while every other thing on the page
	 * — the headline beside it included — stopped at the measure. It read as
	 * something that had escaped the layout.
	 *
	 * This is the same arithmetic the centred column does, written out: half of
	 * whatever the window has over the measure, plus the gutter. `max()` handles
	 * the narrow case, where there is no overhang to split and the gutter is the
	 * whole of it.
	 */
	right: max(var(--gutter), calc((100vw - var(--measure)) / 2 + var(--gutter)));
	width: min(38%, 30rem);
	/* Nothing behind the figure. The canvas is drawn with `alpha: true` and the
	   painted set is off, so the slide's own frame shows through. */
	background: transparent;
	/* Pointer events on, and deliberately: the figure can be turned. It is the
	   one thing on the front page a visitor can play with, and discovering that
	   is most of why it is here rather than a picture. */
	cursor: grab;
}

.hero-stage:active { cursor: grabbing; }

.hero-stage canvas {
	width: 100%;
	height: 100%;
	display: block;
}

/*
 * On a champion slide the copy is given the left half outright.
 *
 * Without this the headline runs under the figure at wide sizes — the body is a
 * centred `--measure` column and knows nothing about the stage sitting on top of
 * it.
 */
.hero-slide--champion .hero-body { max-width: min(var(--measure), 100%); }
.hero-slide--champion .hero-body > * { max-width: 32rem; }

/*
 * The scrim on a champion slide leans the other way.
 *
 * The default darkens the left, which is right when the picture is behind the
 * text. Here the *figure* is on the right and has to stay bright, so the wash
 * fades out before it reaches the stage and the character is not sitting behind
 * a veil.
 */
.hero-slide--champion::after {
	background:
		linear-gradient(90deg, rgb(10 8 6 / .94) 0%, rgb(10 8 6 / .7) 40%, transparent 62%),
		linear-gradient(0deg, rgb(10 8 6 / .9) 0%, transparent 45%);
}

/*
 * The champion slide's own light, since it has no picture.
 *
 * A warm glow rising from behind where the figure stands. Two things it does: it
 * stops the frame reading as an empty black rectangle, and it puts the brightest
 * part of the slide behind the character — which is what a stage light is for and
 * what separates a figure from the background without a box around it.
 *
 * Positioned off the same right edge as the stage, so the light and the character
 * move together when the window changes width.
 */
.hero-slide--champion::before {
	background:
		radial-gradient(
			38rem 26rem at calc(100% - max(var(--gutter), (100vw - var(--measure)) / 2 + var(--gutter)) - 15rem) 105%,
			color-mix(in oklab, var(--gold) 16%, transparent),
			transparent 70%
		),
		var(--void);
}

/*
 * A slide with no media at all — the honest last resort when an install has no
 * art and no ranked players. A flat near-black frame would read as a failed
 * image, so it gets a quiet radial instead: clearly a designed empty state.
 */
.hero-slide--plain::before {
	background:
		radial-gradient(120% 90% at 20% 100%, color-mix(in oklab, var(--gold) 10%, transparent), transparent 60%),
		var(--plate);
}

/*
 * The figure goes when there is no room for it.
 *
 * Below this width the stage and the headline are fighting over the same forty
 * characters, and a hero whose text is unreadable is worse than one without a
 * picture. The character is still on the podium further down the page, so nothing
 * is actually lost.
 */
@media (max-width: 52rem) {
	.hero-stage { display: none; }

	.hero-slide--champion::after {
		background:
			linear-gradient(0deg, rgb(10 8 6 / .95) 0%, rgb(10 8 6 / .5) 60%, transparent 100%);
	}
}

.hero .swiper-pagination { bottom: 1.1rem !important; }

.hero .swiper-pagination-bullet {
	width: 1.6rem;
	height: 3px;
	border-radius: 0;
	background: var(--bone);
	opacity: .3;
	transition: opacity .2s, background .2s;
}

.hero .swiper-pagination-bullet-active { background: var(--accent); opacity: 1; }

/*
 * Our own arrows, inside the frame.
 *
 * Swiper's defaults are chevrons from its icon font, sized in `--swiper-*`
 * variables, and they hang past a rounded edge. Its class names are kept because
 * that is what the library binds to; everything visible is replaced — including
 * the `::after` glyph, which has to be turned off explicitly or it draws over
 * the icon.
 */
.hero .hero-arrow {
	width: 2.6rem;
	height: 2.6rem;
	margin: 0;
	padding: 0;
	display: grid;
	place-items: center;
	background: rgb(8 8 10 / .55);
	border: 1px solid rgb(255 255 255 / .18);
	border-radius: 50%;
	color: var(--bone);
	backdrop-filter: blur(4px);
	transition: background .2s, border-color .2s, color .2s;
}

.hero .hero-arrow::after { content: none; }
.hero .swiper-button-prev { left: clamp(.6rem, 3vw, 1.6rem); }
.hero .swiper-button-next { right: clamp(.6rem, 3vw, 1.6rem); }

.hero .hero-arrow svg {
	width: 1.1rem;
	height: 1.1rem;
	fill: none;
	stroke: currentColor;
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.hero .hero-arrow:hover {
	background: var(--accent);
	border-color: var(--accent);
	color: var(--ink);
}

@media (max-width: 48rem) {
	.hero .hero-arrow { display: none; }
}

/* ── Server status ───────────────────────────────────────────── */

/*
 * A status card, not a row of trivia.
 *
 * What it can honestly report is narrow — this schema has no online count — so
 * the card leads with the two things it *can* stand behind: that the database
 * answered, and how recently the server wrote to it. The gauge underneath is the
 * one proportion on the page that describes the server rather than a player.
 */
.status {
	display: grid;
	grid-template-columns: minmax(12rem, 1fr) minmax(14rem, 2fr) auto;
	gap: 1.6rem;
	align-items: center;
	margin-bottom: 2.4rem;
	padding: 1.1rem 1.3rem;
	background: var(--slab);
	border: 1px solid var(--rule-soft);
	border-radius: var(--round);
}

@media (max-width: 56rem) {
	.status { grid-template-columns: 1fr; gap: 1.1rem; }
}

.status-head { display: flex; align-items: center; gap: .8rem; }

.status-dot {
	width: .6rem;
	height: .6rem;
	border-radius: 50%;
	background: var(--dim);
	flex: 0 0 auto;
}

/* Live is a pulse; stale is a still dot. The difference has to be visible
   without reading the line under it. */
.status-dot.is-live {
	background: var(--good);
	box-shadow: 0 0 0 0 rgb(74 222 128 / .6);
	animation: status-beat 2.4s infinite;
}

@keyframes status-beat {
	70%  { box-shadow: 0 0 0 .5rem rgb(74 222 128 / 0); }
	100% { box-shadow: 0 0 0 0 rgb(74 222 128 / 0); }
}

.status-title {
	margin: 0;
	font-size: .74rem;
	letter-spacing: .18em;
	text-transform: uppercase;
	color: var(--bone);
}

.status-sub { margin: .2rem 0 0; font-size: .74rem; color: var(--dim); }

.status-gauge-head {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	font-size: .74rem;
	color: var(--ash);
}

.status-gauge-head strong { color: var(--accent); font-size: .95rem; }

.status-bar {
	height: 6px;
	margin: .4rem 0 .35rem;
	background: rgb(255 255 255 / .06);
	border-radius: 999px;
	overflow: hidden;
}

.status-bar span {
	display: block;
	width: calc(var(--fill) * 100%);
	height: 100%;
	background: linear-gradient(90deg, var(--accent), var(--accent-soft));
}

.status-note { margin: 0; font-size: .7rem; color: var(--dim); }

.status-figures { display: flex; gap: 1.8rem; }

.status-value {
	margin: 0;
	font-family: var(--display);
	font-weight: 700;
	font-size: 1.3rem;
	line-height: 1;
	color: var(--bone);
	font-variant-numeric: tabular-nums;
}

.status-label {
	margin: .3rem 0 0;
	font-size: .64rem;
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--dim);
}


/* ── Clan podium ─────────────────────────────────────────────── */

/*
 * The same podium the player boards use, with the emblem where the figure goes.
 *
 * A clan cannot be rendered — there is no model of a clan — so the card leans on
 * the one piece of art it does own: the 32×32 mark its members carry on their
 * capes, blown up and left unsmoothed so the pixels read as the badge they are.
 */
.crests {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 1.4rem;
	margin-bottom: 2.6rem;
}

@media (max-width: 56rem) { .crests { grid-template-columns: 1fr; } }

.crest-card {
	--place: var(--rule);
	position: relative;
	padding-top: .8rem;
}

.crest-card.is-rank-1 { --place: #e3b341; }
.crest-card.is-rank-2 { --place: #c9ccd1; }
.crest-card.is-rank-3 { --place: #c07a3e; }

.crest-tab {
	position: absolute;
	top: 0;
	left: 50%;
	z-index: 2;
	transform: translateX(-50%);
	margin: 0;
	padding: .2rem .8rem;
	background: var(--ink);
	border: 1px solid var(--place);
	border-radius: var(--round);
	color: var(--place);
	font-size: .64rem;
	font-weight: 600;
	letter-spacing: .2em;
	text-transform: uppercase;
	white-space: nowrap;
}

/*
 * `.crest-link`, not `.crest-body`.
 *
 * The clan detail page already owns `.crest-body` for the block beside the
 * emblem, and giving the podium card's anchor the same name laid that block out
 * as a flex row — name, stats and the clan's notice all on one line. Two
 * components, two names.
 */
.crest-link {
	display: flex;
	align-items: center;
	gap: 1rem;
	position: relative;
	padding: 1.3rem 1.2rem 1.2rem;
	background: linear-gradient(180deg, rgb(255 255 255 / .02), transparent 60%);
	border: 1px solid var(--place);
	border-radius: var(--round);
	text-decoration: none;
	overflow: hidden;
}

.crest-link:hover { background: rgb(255 255 255 / .03); }

.crest-rank {
	position: absolute;
	top: .3rem;
	right: .7rem;
	font-family: var(--display);
	font-weight: 700;
	font-size: 2.8rem;
	line-height: 1;
	color: rgb(255 255 255 / .05);
	pointer-events: none;
}

.crest-badge {
	flex: 0 0 auto;
	width: 64px;
	height: 64px;
	display: grid;
	place-items: center;
	background: rgb(255 255 255 / .03);
	border: 1px solid var(--rule-soft);
	border-radius: var(--round);
}

.crest-badge img { image-rendering: pixelated; display: block; }

.crest-plate { min-width: 0; }

.crest-title {
	display: block;
	font-family: var(--display);
	font-weight: 700;
	font-size: 1.15rem;
	color: var(--bone);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.crest-link:hover .crest-title { color: var(--place); }
.crest-meta { display: block; margin-top: .15rem; font-size: .72rem; color: var(--ash); }

.crest-value {
	display: block;
	margin-top: .5rem;
	font-size: 1rem;
	font-weight: 600;
	color: var(--place);
	font-variant-numeric: tabular-nums;
}

/* A member's donation, called out inside the meta line — it is the one number
   on a roster that is a contribution rather than a score. */
/*
 * The donation, under the points and in the same column.
 *
 * Both numbers are national points — one held, one given away — so they are set
 * the same way: figure, then a small unit label, right-aligned under each other.
 * Only the colour separates them.
 */
.gift {
	display: block;
	margin-top: .2rem;
	font-size: .8rem;
	font-weight: 500;
	color: var(--good);
}

.gift .entry-unit { color: var(--good); opacity: .7; }

.crest-fund { margin: .3rem 0 0; font-size: .82rem; color: var(--ash); }
.crest-fund strong { color: var(--good); font-weight: 600; }

/* ── The masthead on a narrow screen ─────────────────────────── */

/*
 * Below 62rem the bar collapses to logo · play · burger, and the navigation
 * drops out of the flow into a panel underneath.
 *
 * The state is a checkbox rather than a script: a hamburger is one boolean, CSS
 * can hold a boolean, and navigation that stops working when a script fails is
 * a poor trade for a class toggle.
 */
.burger-state { position: absolute; opacity: 0; pointer-events: none; }
.burger { display: none; }

@media (max-width: 62rem) {
	.masthead-inner {
		flex-wrap: wrap;
		gap: .8rem;
		padding-block: .7rem;
	}

	.brand { margin-right: auto; }

	/* Three bars drawn from one element: the middle is the box, the other two
	   are its pseudo-elements. */
	.burger {
		display: grid;
		place-items: center;
		width: 2.4rem;
		height: 2.4rem;
		border: 1px solid var(--rule);
		border-radius: var(--round);
		cursor: pointer;
		order: 3;
	}

	.burger span,
	.burger span::before,
	.burger span::after {
		content: '';
		display: block;
		width: 1.1rem;
		height: 2px;
		background: var(--bone);
		transition: transform .2s, opacity .2s;
	}

	.burger span { position: relative; }
	.burger span::before { position: absolute; top: -.4rem; }
	.burger span::after { position: absolute; top: .4rem; }

	/* Open: the bars become a cross. */
	.burger-state:checked ~ .burger span { background: transparent; }
	.burger-state:checked ~ .burger span::before { transform: translateY(.4rem) rotate(45deg); }
	.burger-state:checked ~ .burger span::after { transform: translateY(-.4rem) rotate(-45deg); }

	.play { order: 2; }

	.find {
		order: 4;
		width: 100%;
	}

	.find-input { width: 100%; }

	.nav {
		order: 5;
		display: none;
		width: 100%;
		flex-direction: column;
		align-items: stretch;
		gap: 0;
		margin: 0;
		border-top: 1px solid var(--rule-soft);
	}

	.burger-state:checked ~ .nav { display: flex; }

	.nav-link {
		padding: .8rem .2rem;
		border-bottom: 1px solid var(--rule-soft);
	}

	.nav-link.is-here::after,
	.menu.is-here > .nav-link::after { inset: auto .2rem 0 .2rem; }

	/*
	 * The dropdown stops being a dropdown.
	 *
	 * On a phone there is nothing to hover and nowhere to float; the ranking
	 * links simply become part of the list, indented under their heading.
	 */
	.menu-panel {
		position: static;
		display: block;
		opacity: 1;
		visibility: visible;
		transform: none;
		padding: 0 0 .6rem .8rem;
		background: none;
		border: 0;
		box-shadow: none;
	}

	.menu-arrow { display: none; }
	.menu-head { margin-top: .8rem; }
}

/* ── Tabs ────────────────────────────────────────────────────── */

.tabs {
	display: flex;
	flex-wrap: wrap;
	gap: .3rem;
	margin-bottom: .8rem;
}

.tabs.is-sub { margin-bottom: 1.8rem; }

.tab {
	padding: .5rem 1.1rem;
	border: 1px solid var(--rule-soft);
	border-radius: var(--round);
	color: var(--ash);
	font-size: .78rem;
	text-decoration: none;
}

.tab:hover { border-color: var(--rule); color: var(--bone); }

.tab.is-here {
	border-color: var(--accent);
	color: var(--accent);
	background: rgb(217 164 65 / .08);
}

.tabs.is-sub .tab { padding: .35rem .8rem; font-size: .72rem; }

/* A gear row leads with the item, so the icon takes the rank's neighbour slot. */
.entry.is-gear { grid-template-columns: 2.6rem 45px minmax(0, 1fr) auto; }

.gear-icon {
	width: 45px;
	height: 45px;
	display: grid;
	place-items: center;
	background: rgb(255 255 255 / .03);
	border: 1px solid var(--rule-soft);
	border-radius: var(--round);
	cursor: default;
}

.gear-icon img { image-rendering: pixelated; display: block; }

.ic-set-note { margin: 0 0 .3rem; font-size: .72em; color: #969696; }

/*
 * The engraving on an empty square, and the rarity on a full one.
 *
 * Both come from the editors, where they were worked out on a rack of
 * fifty-one squares, and both are the item's own facts rather than decoration:
 * the drawing says what belongs in a slot, and the colour is the one the item
 * card prints the name in. Sharing them is why the two pages read as one site.
 */
.cell-glyph {
	width: 55%;
	height: 55%;
	fill: none;
	stroke: var(--rule);
	stroke-width: 1.5;
	stroke-linecap: round;
	stroke-linejoin: round;
	opacity: .5;
}

/* Inset, so a square does not change size when it fills — in a grid, that is
   the difference between a rack and a jitter. */
.cell[data-item] {
	box-shadow:
		inset 0 0 0 1px var(--rarity, transparent),
		inset 0 0 0 1px rgb(255 255 255 / .04),
		0 0 9px -3px var(--rarity, transparent);
}

.glyphs { display: none; }

/*
 * The kings, inside the state band.
 *
 * Two cards, side by side where there is room and stacked where there is not.
 * `auto-fit` rather than a breakpoint for the same reason the facts above use it:
 * the decision belongs to the container's width, not the window's.
 */
.state-crowns {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
	gap: var(--s3);
	padding-top: var(--s4);
	border-top: 1px solid var(--seam-soft);
}

/*
 * ═════════════════════════════════════════════════════════════════════════════
 *  THE FORGE — what makes this a game server's site and not a dashboard
 * ═════════════════════════════════════════════════════════════════════════════
 *
 * The honest criticism this answers: with the palette and the type sorted, the
 * panel still read as a tasteful admin dashboard with gold accents. Soft
 * ten-pixel corners, hairline borders, generous air, no texture anywhere. Nothing
 * about it said Knight Online, and a server's front page has one job before all
 * others, which is to look like the game it belongs to.
 *
 * ── Where this comes from ────────────────────────────────────────────────────
 *
 * Not invented. The client's own interface atlas ships converted in
 * `assets/ui/re_various_frame.png`, and it was read before any of this was
 * written. The game's windows are:
 *
 *   · hard-cornered stone-and-metal plates, never rounded;
 *   · bevelled — a light top edge, a dark bottom edge, lit from above;
 *   · fitted with **ornate gold corner pieces**, which is the single most
 *     recognisable thing about them;
 *   · headed by a gold title bar with the label inset into it;
 *   · filled with *recessed* rows — list items sunk into the plate, not floating
 *     on it.
 *
 * The ornaments themselves are **not** pasted in. Measured off the atlas they are
 * 28×32 pixels of green-tinted filigree; scaled up for a browser they turn to
 * mush, and dropped onto a warm gold page they read as something copied from
 * another program. So the grammar is taken and redrawn in CSS — crisp at any
 * pixel density, in this palette, and ours.
 *
 * ── The one device ───────────────────────────────────────────────────────────
 *
 * `.plate`. Every container becomes one, and the corner brackets are what carry
 * it. One device repeated everywhere is what makes an interface feel designed;
 * five different decorations is what makes it feel assembled. Everything else
 * here — the bevel, the plaque, the dust — is quiet on purpose so the brackets
 * are the thing you remember.
 */

:root {
	/*
	 * Radius: gone.
	 *
	 * The game has no rounded window anywhere, and a 10px corner was the single
	 * biggest reason the page read as a web app. `--round-pill` stays, because a
	 * badge is a badge rather than a plate.
	 */
	--round-card: 0px;
	--round-data: 0px;

	/*
	 * The bevel, as one token.
	 *
	 * Three shadows and each does a job: a light inner top edge (the light falls
	 * from above), a dark inner bottom edge (so the plate has thickness), and a
	 * hard drop with no blur — which is how a metal plate sits on a surface, where
	 * a soft shadow is how a card floats over one.
	 */
	--bevel:
		inset 0 1px 0 #ffffff1c,
		inset 0 -1px 0 #00000099,
		0 2px 0 #00000073;

	/* The bracket's arm length and how far it sits in from the plate's edge. */
	--fitting: 11px;
	--fitting-inset: 5px;
}

/*
 * ── The plate ────────────────────────────────────────────────────────────────
 *
 * Applied through the class names the views already use rather than by adding a
 * `.plate` to two hundred elements: the markup says what a thing *is*, and what a
 * container looks like is this stylesheet's business.
 */
/*
 * ── What is a plate and what is not ──────────────────────────────────────────
 *
 * A first pass plated everything, including `.board`, `.strip` and `.week` — and
 * those are not containers. They are a heading followed by a list, with no box of
 * their own in the markup and none in the game either. Framing them made every
 * section a frame inside a frame, which is what "the boxes are out of proportion"
 * was: a plate around a plate around one table row.
 *
 * So a plate is an **object** — something with edges of its own that you could
 * point at in the game: the state band, the next-war card, a pedestal, a news
 * card. A *section* gets the plaque heading instead, which is the game's own
 * grammar for one: a title and a rule, not a box.
 */
.state,
.upnext,
.podium .pedestal,
.card,
.status,
.crown,
.fact {
	position: relative;
	border: 1px solid var(--seam);
	background:
		/* The plate's own light, strongest at the top edge. */
		linear-gradient(180deg, #ffffff0a, transparent 34%),
		var(--plate);
	box-shadow: var(--bevel);
}

/*
 * The gold corner fittings.
 *
 * Eight gradient layers on one pseudo-element: two arms per corner, each a 1px
 * line of a fixed length. Gradients rather than four extra elements because a
 * bracket is decoration and decoration should not be in the document, and rather
 * than a border-image because this stays exactly one physical pixel at any zoom.
 *
 * `inset` pulls them off the edge, which is what makes them read as *fittings* on
 * a plate instead of as a second border around it.
 */
.state::before,
.upnext::before,
.podium .pedestal::before,
.card::before,
.status::before {
	content: '';
	position: absolute;
	inset: var(--fitting-inset);
	pointer-events: none;
	background:
		/* top-left */
		linear-gradient(var(--gold-deep), var(--gold-deep)) left    top    / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) left    top    / 1px var(--fitting) no-repeat,
		/* top-right */
		linear-gradient(var(--gold-deep), var(--gold-deep)) right   top    / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) right   top    / 1px var(--fitting) no-repeat,
		/* bottom-left */
		linear-gradient(var(--gold-deep), var(--gold-deep)) left    bottom / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) left    bottom / 1px var(--fitting) no-repeat,
		/* bottom-right */
		linear-gradient(var(--gold-deep), var(--gold-deep)) right   bottom / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) right   bottom / 1px var(--fitting) no-repeat;
}

/*
 * A hovered plate lights its fittings.
 *
 * The only animation on the device, and it is on the plates that are links —
 * which is also how a reader learns which ones are. Colour, not movement: a plate
 * that lifts on hover is a card, and this is meant to be bolted down.
 */
.fact::before,
.crown::before {
	content: '';
	position: absolute;
	inset: var(--fitting-inset);
	pointer-events: none;
	transition: background-image .18s;
	background:
		linear-gradient(var(--gold-deep), var(--gold-deep)) left    top    / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) left    top    / 1px var(--fitting) no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) right   bottom / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) right   bottom / 1px var(--fitting) no-repeat;
}

.fact:hover::before,
.crown:hover::before {
	background:
		linear-gradient(var(--gold-hot), var(--gold-hot)) left    top    / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-hot), var(--gold-hot)) left    top    / 1px var(--fitting) no-repeat,
		linear-gradient(var(--gold-hot), var(--gold-hot)) right   bottom / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-hot), var(--gold-hot)) right   bottom / 1px var(--fitting) no-repeat;
}

/*
 * ── Recessed rows ────────────────────────────────────────────────────────────
 *
 * The game sinks its list rows into the plate rather than standing them on it,
 * and it is a bigger part of the look than the frames are — it is why a KO window
 * reads as carved rather than as printed.
 *
 * An inverted bevel does the whole thing: dark at the top where a raised edge
 * would be light.
 */
.fact,
.slot,
.crown {
	background:
		linear-gradient(180deg, #00000059, transparent 40%),
		var(--void);
	box-shadow:
		inset 0 1px 0 #00000099,
		inset 0 -1px 0 #ffffff0d;
}

/*
 * ── The plaque ───────────────────────────────────────────────────────────────
 *
 * A section heading, the way the game heads a window: the label, then a gold rule
 * running out to the edge, with a lozenge marking the join. The rule fades rather
 * than stopping, so it reads as light catching an edge instead of as a border.
 *
 * Applied to the headings that open a block. Not to every `h2` on the site — a
 * plaque on a subheading inside a card is the device wearing out.
 */
/*
 * On the heading itself, not on the row that holds it.
 *
 * `.week-head` is a flex row with the title at one end and a summary at the other,
 * so putting the plaque's rule on *it* dropped the rule after the summary — the
 * pseudo-element is the container's last child, not the title's neighbour. The
 * plaque belongs to the heading, and the heading grows to fill the row.
 */
.week-title,
.board-title,
.strip-title,
.plaque {
	display: flex;
	align-items: center;
	gap: var(--s3);
	flex: 1;
}

.week-title::after,
.board-title::after,
.strip-title::after,
.plaque::after {
	content: '';
	flex: 1;
	height: 1px;
	background: linear-gradient(90deg, var(--gold-deep), transparent);
}

/* The lozenge. A rotated square, because the game's own fittings are diamonds. */
.week-title::before,
.board-title::before,
.strip-title::before,
.plaque::before {
	content: '';
	width: 5px;
	height: 5px;
	flex: 0 0 auto;
	background: var(--gold);
	transform: rotate(45deg);
}

/*
 * ── The page's air ───────────────────────────────────────────────────────────
 *
 * Two textures, both the client's own art, both nearly invisible — and between
 * them they are most of the difference between "a website" and "a game's site".
 * A flat near-black field is what an admin panel looks like.
 *
 * `dust.png` tiles at 256 and sits fixed, so scrolling moves the content across a
 * still field rather than dragging the texture with it. `god_rays1` hangs behind
 * the top of the page where the hero is.
 *
 * Both on `body::before`, one element, `pointer-events: none`, and *behind*
 * everything — `z-index: -1` with the body as the containing block.
 */
body {
	position: relative;
	isolation: isolate;
}

body::before {
	content: '';
	position: fixed;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	background:
		url('/img/god_rays1.png') top center / 120rem auto no-repeat,
		url('/img/dust.png') repeat;
	/* Low enough that nobody sees a texture and high enough that everybody sees
	   the page is not flat. Measured by turning it off and on. */
	opacity: .07;
}

/*
 * ── The masthead, forged ─────────────────────────────────────────────────────
 *
 * The bar was a flat strip with a hairline under it. Now it is the top plate of
 * the page: a dark gradient with a bevelled bottom edge, and the war line glowing
 * in the seam.
 */
.masthead {
	background:
		linear-gradient(180deg, #1b140ee6, #0d0a07f2);
	backdrop-filter: blur(10px);
	box-shadow:
		inset 0 1px 0 #ffffff12,
		0 1px 0 #000000cc,
		0 8px 24px -12px #000000cc;
}

/*
 * The war line, lit.
 *
 * It is a seam in metal rather than a border on a box, so it gets a glow above
 * it — the light a hot line would throw onto the plate it is cut into. This is
 * the signature and it is the one place the page is allowed to emit light.
 */
/*
 * The glow belongs to each side, not to the bar.
 *
 * A `currentColor` shadow on the bar itself stood here for a moment and it threw a
 * *white* halo — `currentColor` on that element is the page's text colour. The
 * light has to come from the two nations, which means it belongs on the two sides.
 */
.masthead .warline-side--karus {
	box-shadow: 0 0 10px -1px color-mix(in oklab, var(--karus) 60%, transparent);
}

.masthead .warline-side--elmorad {
	box-shadow: 0 0 10px -1px color-mix(in oklab, var(--elmorad) 60%, transparent);
}

/*
 * ── Buttons as struck metal ──────────────────────────────────────────────────
 *
 * The one filled control on the page. The game's buttons are domed gold with a
 * dark rim, and two gradients plus the bevel get there without an image: a
 * vertical light-to-dark for the dome, and a dark rim from the border.
 */
.play,
.hero-go {
	background: linear-gradient(180deg, var(--gold-hot), var(--gold) 45%, var(--gold-deep));
	color: var(--gold-ink);
	border: 1px solid #00000073;
	box-shadow:
		inset 0 1px 0 #fff6,
		inset 0 -1px 0 #0006,
		0 2px 0 #00000080;
	text-shadow: 0 1px 0 #ffffff40;
}

.play:hover,
.hero-go:hover {
	background: linear-gradient(180deg, #ffe9ab, var(--gold-hot) 45%, var(--gold));
}

/* Pressed, and it actually moves — a button that does not is a picture. */
.play:active,
.hero-go:active {
	transform: translateY(1px);
	box-shadow: inset 0 1px 0 #fff4, inset 0 -1px 0 #0006, 0 1px 0 #00000080;
}

/*
 * The outline button is the same plate as everything else, so the pair reads as
 * "one struck, one cut" rather than as two unrelated shapes.
 */
.hero-alt {
	border: 1px solid var(--seam);
	background: linear-gradient(180deg, #ffffff0a, transparent 40%), var(--plate);
	box-shadow: var(--bevel);
	color: var(--parch);
}

.hero-alt:hover {
	border-color: var(--gold-deep);
	color: var(--gold-hot);
}

/*
 * ── Clearance for the fittings ───────────────────────────────────────────────
 *
 * The brackets sit 5px in from the plate's edge and run 11px along it, so
 * anything closer than about 16px to a corner collides with one. That is what put
 * the ladder's heading on top of its own corner piece and clipped the record count
 * against the other.
 *
 * Stated once here rather than trusted to each container's own padding: a plate
 * that gains the decoration should gain the room for it in the same rule, or the
 * next plate added will have the same bug.
 */
.state,
.upnext,
.card,
.status {
	padding: var(--s5);
}

.podium .pedestal { padding: 0; }

/*
 * The pedestal is the exception, and deliberately.
 *
 * Its figure fills the frame edge to edge — that is the whole point of the card —
 * so the padding goes on the *plate* inside it instead. The nameplate under the
 * figure gets the clearance; the stage does not need it, because a corner bracket
 * over the top of a 3D character reads as a frame around a portrait rather than
 * as a collision.
 */
.pedestal-plate,
.pedestal-name,
.pedestal-foot {
	padding-left: var(--s4);
	padding-right: var(--s4);
}

/*
 * ── Sections, unframed ───────────────────────────────────────────────────────
 *
 * With the box gone, a section is a plaque and a list. The heading needs the rule
 * to run to the page's edge, so it carries no side padding at all — which is the
 * opposite of a plate and is exactly why the two should not have shared a rule.
 */
.board,
.strip,
.week {
	padding: 0;
	border: 0;
	background: none;
	box-shadow: none;
}

/*
 * ── The podium, in proportion ────────────────────────────────────────────────
 *
 * A fixed three-column grid is right for a podium with three on it and wrong for
 * every other case. A ladder for a young server has one ranked player, and one
 * card stretched down a third of the page was the tall narrow sliver in the
 * corner: the grid kept its columns and the card kept the aspect ratio, so it grew
 * until it was five hundred pixels of empty stage.
 *
 * `auto-fit` with a **ceiling** as well as a floor fixes both ends. Three cards
 * sit at their natural size; one sits at that same size, centred, looking like a
 * deliberate single card rather than a broken row of three.
 */
/*
 * The podium takes the page.
 *
 * It was capped at 20rem a card and centred, to stop a lone card stretching down a
 * third of the page — which it fixed, and left three cards huddled in the middle of
 * a wide screen with a hand's width of empty on either side. Dry is the right word
 * for it.
 *
 * The cap belongs on the **stage's height**, not on the card's width. A card is then
 * free to be as wide as its share of the row, and a single one comes out wide and
 * short rather than tall and narrow — which is the shape that was wrong in the first
 * place.
 */
.podium {
	/*
	 * Columns from the number of cards, not from arithmetic.
	 *
	 * Three attempts and each failed differently. A 20rem cap huddled three cards in
	 * the middle of a wide screen. `1fr` gave a lone card the whole width and a
	 * character adrift in it. A 26rem cap was meant to be "exactly a third" and was
	 * 0.4rem too generous — 3 × 26 plus two 1rem gaps is 80rem against a 78.8rem
	 * measure, so the third card wrapped onto its own line.
	 *
	 * `:has()` removes the arithmetic entirely. Count the children, set that many
	 * `1fr` columns, and the row fills exactly by construction — there is no number
	 * to get wrong. A lone card is capped instead, because a full-width pedestal is
	 * a billboard.
	 */
	display: grid;
	gap: var(--s4);
	justify-content: center;
	grid-template-columns: minmax(0, 26rem);
}

.podium:has(> :nth-child(2)) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.podium:has(> :nth-child(3)) { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/*
 * Two across on a tablet and one on a phone, whatever the count. Three 3D figures
 * side by side at 40rem is three postage stamps.
 */
@media (max-width: 62rem) {
	.podium:has(> :nth-child(3)) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 40rem) {
	.podium,
	.podium:has(> :nth-child(2)),
	.podium:has(> :nth-child(3)) { grid-template-columns: minmax(0, 1fr); }
}

/*
 * And the figure gets a shorter frame.
 *
 * 4:5 was drawn for three cards across a wide page, where each is about 20rem and
 * the stage comes out 25rem tall. Alone at the same width it read as a column. 5:6
 * is close enough to keep the character's feet in frame and short enough that the
 * card is a card.
 */
/*
 * Taller now that the nameplate sits inside it.
 *
 * 5:6 was the height of the figure alone, with the plate stacked under it. With
 * the plate overlaid the stage *is* the card, so it has to carry both — and the
 * bottom fifth is now scrim rather than character, which is why the frame grows
 * rather than the figure shrinking inside the old one.
 */
.pedestal-stage { aspect-ratio: 4 / 5; }

/*
 * ── The hero, given its full height ──────────────────────────────────────────
 *
 * It was `clamp(20rem, 42vw, 30rem)` — a banner. Next to the reference the
 * difference was not subtle: theirs fills the window down to about three quarters
 * and dissolves into the page, ours stopped at a hard line halfway up the screen
 * and looked like a header image.
 *
 * `78vh` rather than `100vh`. A hero that exactly fills the viewport tells a
 * first-time visitor there is nothing below it; leaving a quarter of the next
 * block showing is what makes them scroll, and it costs nothing.
 *
 * `dvh` because on a phone `vh` is the height *without* the browser's own bars, so
 * a `100vh` hero is taller than the screen until the address bar hides — the
 * classic mobile jump. `dvh` follows the visible viewport.
 */
.hero {
	height: clamp(28rem, 78dvh, 46rem);
	/*
	 * Pulled up under the content that follows it.
	 *
	 * The negative margin is the overlap: the next block rises onto the hero's
	 * last few centimetres, so the two are joined rather than stacked. Paired with
	 * the fade below, the video appears to pass *behind* the page.
	 */
	/*
	 * The overlap, deepened.
	 *
	 * At -3.5rem the two blocks merely touched. At -7rem the state band clearly
	 * sits *on* the hero, which is what joins them into one composition — and it is
	 * possible only because the curtain above is now tall enough that the band
	 * lands on solid page colour rather than on half a video.
	 */
	margin-bottom: -3rem;
}

/*
 * The bottom fade, on the hero and not on a slide.
 *
 * On the slide it would be reapplied per slide and would slide with it — the
 * gradient would visibly travel sideways every time the slider paged. On the hero
 * it stands still while the pictures move underneath, which is what makes it read
 * as the page's own edge rather than as part of the image.
 *
 * `to var(--void)` and not to black: the page is warm near-black, and a fade to
 * pure black leaves a dark band sitting on a lighter page.
 */
.hero::after {
	content: '';
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	/*
	 * Tall, and the height is the point.
	 *
	 * At 12rem the video still ended in a visible line — a fade that short is a
	 * band rather than a dissolve, and the eye finds the top of it. At 22rem the
	 * curtain covers the whole lower third: the picture *becomes* the page instead
	 * of stopping on it.
	 */
	height: 22rem;
	z-index: 3;
	pointer-events: none;
	/*
	 * Five stops, not two.
	 *
	 * A two-stop linear gradient fades on a straight line, and a straight ramp in
	 * opacity does not look straight — the human eye is roughly logarithmic about
	 * brightness, so the midpoint reads as a distinct edge. Easing the stops (0.35
	 * at 30%, 0.72 at 58%) makes the darkening feel even, which is what "smooth"
	 * actually means here.
	 */
	background: linear-gradient(
		180deg,
		transparent 0%,
		color-mix(in oklab, var(--void) 35%, transparent) 30%,
		color-mix(in oklab, var(--void) 72%, transparent) 58%,
		color-mix(in oklab, var(--void) 94%, transparent) 80%,
		var(--void) 100%
	);
}

/*
 * The block that rises onto the hero.
 *
 * Named directly, after two attempts at being clever about it. `main > .hero + *`
 * was first and landed on the champion's `<script>` — the block it was meant for
 * stayed under the hero with its heading cut off. A chain of `+ script + script +`
 * fixed that and was worse: a rule nobody could read and one more script away from
 * breaking again.
 *
 * It named `.state` next, and then the state band was removed and the rule went with
 * it — so the two-column grid that replaced it went straight back under the curtain,
 * with the news heading and the sidebar's titles eaten. Same bug, third time.
 *
 * So both candidates are named, and the overlap is smaller than it was. The deep
 * -7rem was for a band *designed* to sit on the hero; a news panel is not, and its
 * heading has to clear the fade. The padding is what guarantees that rather than the
 * margin arithmetic happening to work out.
 */
.state,
.home {
	position: relative;
	z-index: 4;
}

.home { padding-top: var(--s6); }

/* The controls clear the fade rather than sitting inside it. */
.hero .swiper-pagination { bottom: 8.5rem !important; }

/*
 * On a short landscape window — a laptop at 768px tall, or a phone turned
 * sideways — 78dvh leaves no room for a headline and two buttons. The floor takes
 * over and the hero is allowed to be taller than the window; scrolling is a
 * smaller cost than a cropped sentence.
 */
@media (max-height: 34rem) {
	.hero { height: 26rem; }
	.hero .swiper-pagination { bottom: 3rem !important; }
}

/*
 * ── The four cells ───────────────────────────────────────────────────────────
 *
 * Four rather than two, and denser: the next war, both kings, and the server. At
 * four across they are compact enough that the whole band is one glance, and
 * `auto-fit` folds them to two and then one as the window narrows.
 *
 * `minmax(13rem, 1fr)` — 13rem is the width at which the longest thing in a cell
 * (a king's name, and this server has an eighteen-character one) still fits on one
 * line. Measured against the real data rather than picked.
 */
.state-facts {
	grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
	gap: var(--s2);
}

.fact { padding: var(--s3); }

/*
 * The nation owns a crown's colour, because here the nation *is* the data. The
 * left edge only — a whole cell in Karus red beside one in El Morad blue reads as
 * two warnings rather than as two kings.
 */
.fact--crown { border-left: 2px solid var(--side, var(--seam)); }
.fact--crown.nation-karus { --side: var(--karus); }
.fact--crown.nation-elmorad { --side: var(--elmorad); }
.fact--crown .eyebrow { color: color-mix(in oklab, var(--side, var(--parch-faint)) 70%, var(--parch-faint)); }

.fact--war { border-left: 2px solid var(--gold-deep); }
.fact--pulse { border-left: 2px solid var(--seam); }

.fact-note {
	margin: 0;
	font-size: .76rem;
	color: var(--parch-faint);
}

.fact-pct { margin-left: auto; color: var(--parch); }

/*
 * ── The meter ────────────────────────────────────────────────────────────────
 *
 * One bar, one job, and it grows in on load.
 *
 * The animation is a **transform**, not a width: a width transition relayouts the
 * bar every frame and on a page that is also starting a WebGL context that shows
 * as a stutter. `scaleX` from a left origin costs the compositor nothing.
 *
 * The fill's final value is the inline `--fill`, so the number and the bar cannot
 * disagree — and with the animation suppressed the bar is simply already at its
 * value rather than absent.
 */
.meter {
	display: block;
	height: 4px;
	margin: var(--s1) 0;
	background: var(--seam-soft);
	overflow: hidden;
}

.meter-fill {
	display: block;
	height: 100%;
	width: var(--fill, 0%);
	background: linear-gradient(90deg, var(--gold-deep), var(--gold));
	transform-origin: left center;
	animation: meter-grow .9s cubic-bezier(.22, 1, .36, 1) both;
}

@keyframes meter-grow {
	from { transform: scaleX(0); }
	to   { transform: scaleX(1); }
}

@media (prefers-reduced-motion: reduce) {
	.meter-fill { animation: none; }
}

/*
 * ── Casing, and the Turkish dotted I ─────────────────────────────────────────
 *
 * `text-transform: uppercase` is **language-sensitive**, and this page declares
 * `lang="tr"`. In Turkish the capital of `i` is `İ`, so the browser was doing
 * exactly as it was told and turning the game's English names into `DRUİD`,
 * `NİGHTWOLF` and `PENETRATOR`'s neighbours. Correct for a Turkish word; wrong for
 * every proper noun the game supplies.
 *
 * Two fixes, because there are two different mistakes underneath:
 *
 * 1. **A name is not uppercased at all.** A player wrote `NightWolf` with a
 *    capital W in the middle and a clan wrote `LegionSquad`; flattening those to
 *    caps throws away something the person chose. That was a regression from the
 *    global heading rule — `h1` had no transform before it, and a character's name
 *    is the one heading on this site that is somebody's property rather than the
 *    interface's wording.
 *
 * 2. **English game terms are marked as English.** The class and race strip stays
 *    uppercase because it is a label strip, so it carries `lang="en"` in the markup
 *    and the browser applies English casing to it. That is also simply true, and a
 *    screen reader reading `Pury Tuarek` with Turkish phonemes is its own small
 *    bug.
 */
.plate-name,
.pedestal-name,
.clan,
.crown-name,
.ladder-name {
	text-transform: none;
}

/*
 * Anything marked as English gets English casing wherever it is uppercased.
 *
 * A belt-and-braces rule: `lang` alone is enough for a *correct* browser, and this
 * makes the intent visible in the stylesheet next to the explanation rather than
 * only in the markup.
 */
[lang='en'] {
	text-transform: inherit;
}

/*
 * ═════════════════════════════════════════════════════════════════════════════
 *  THE FRONT PAGE, BELOW THE HERO
 * ═════════════════════════════════════════════════════════════════════════════
 *
 * What this replaces: six full-width bands stacked down the page — a row of fact
 * cells, a podium, a news strip, two ladders — every one given the same weight and
 * the same width. It read as a dashboard because it was built like one, and a stack
 * of equal bands cannot express the thing a front page most needs to express, which
 * is that some of it matters more than the rest.
 *
 * The shape now:
 *
 *     ┌─ Cephe ──────────────────────────────────────────┐   full width
 *     ┌─ Elit: three characters, live ───────────────────┐   full width
 *     ┌─ Haberler ───────────────────┐ ┌─ Sunucu ──────┐    two columns
 *     │  lead + four briefs          │ │ ● indir       │
 *     └──────────────────────────────┘ └───────────────┘
 *     ┌─ En iyiler [tabs] ───────────┐ ┌─ Savaş ───────┐
 *     │  players | clans             │ │ Krallar       │
 *     └──────────────────────────────┘ └───────────────┘
 *
 * The two things nobody else has go across the top at full width — the war balance
 * and three real characters in their gear. Everything after that is a reading
 * column and an acting column, which is the arrangement every site of this kind
 * arrives at, for the reason that people read on the left and click on the right.
 */

/* ── A full-width band ────────────────────────────────── */

.band { margin: 0 0 var(--s7); }

.band-title {
	font-size: 1rem;
	margin-bottom: var(--s4);
}

/* ── The two columns ──────────────────────────────────── */

/*
 * `minmax(0, 2fr)` and not `2fr`.
 *
 * A bare `2fr` is `minmax(auto, 2fr)`, and `auto` on a grid item means "at least as
 * wide as your longest unbreakable content" — a news headline with a long word in it
 * then pushes the column past its share and the sidebar is squeezed. Zero as the
 * floor lets the column be exactly two thirds and lets the text wrap, which is what
 * text is for.
 */
.home {
	display: grid;
	grid-template-columns: minmax(0, 2fr) minmax(17rem, 1fr);
	gap: var(--s5);
	align-items: start;
	margin-bottom: var(--s8);
}

.home-main,
.home-side {
	display: grid;
	gap: var(--s5);
	/* Content-sized rows, so a short card stays short instead of stretching to
	   match its neighbour in the other column. */
	align-content: start;
}

/*
 * One breakpoint, and it is the width at which the sidebar stops being a sidebar.
 *
 * Below 60rem the two columns leave the news about twenty-eight characters a line,
 * which is a newspaper column rather than a paragraph. The sidebar goes underneath
 * — and it goes *under*, not over, because by then the reader has scrolled past the
 * hero's own download button.
 */
@media (max-width: 60rem) {
	.home { grid-template-columns: minmax(0, 1fr); }
}

/* ── A panel in the reading column ────────────────────── */

.panel {
	display: grid;
	gap: var(--s4);
}

.panel-head {
	display: flex;
	align-items: center;
	gap: var(--s4);
}

.panel-title { font-size: 1rem; }

.panel-more {
	flex: 0 0 auto;
	font: 600 .7rem/1 var(--ui);
	letter-spacing: .12em;
	text-transform: uppercase;
	color: var(--parch-faint);
	text-decoration: none;
	transition: color .15s;
}

.panel-more:hover { color: var(--gold-hot); }

/* At the foot of a tab page the link is centred and given room, so it reads as the
   end of the list rather than as another row in it. */
.panel-more--foot {
	display: block;
	padding: var(--s3) 0 0;
	text-align: center;
	border-top: 1px solid var(--seam-soft);
}

/* ── News ─────────────────────────────────────────────── */

/*
 * The lead article, and it is typographic on purpose.
 *
 * Every row in this table names an image and every one of them points at a path
 * this install does not have — the rows were imported from elsewhere. A broken
 * picture is worse than none, so the card is built out of what is actually there:
 * the category, the headline at size, the opening lines, the date. Which is how a
 * newspaper front page worked for two hundred years before it had photographs.
 */
.lead {
	display: grid;
	gap: var(--s2);
	padding: var(--s5);
	border: 1px solid var(--seam);
	background:
		/* The wash leans out of the top-left corner, so the headline sits in the
		   light and the card has a direction. */
		radial-gradient(80% 120% at 0% 0%, var(--gold-veil), transparent 60%),
		var(--plate);
	box-shadow: var(--bevel);
	text-decoration: none;
	transition: border-color .18s;
}

.lead:hover { border-color: var(--gold-deep); }

.lead-tag,
.brief-tag {
	justify-self: start;
	font: 600 .64rem/1 var(--ui);
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var(--gold);
}

.lead-title {
	font-family: var(--display);
	font-weight: 700;
	/* Not uppercased. A headline is a sentence somebody wrote, and caps at this
	   length is a shout — the display face carries it without the transform. */
	text-transform: none;
	letter-spacing: .01em;
	font-size: clamp(1.15rem, 2.4vw, 1.6rem);
	line-height: 1.2;
	margin: 0;
	color: var(--parch);
}

.lead-text {
	margin: 0;
	/* Three lines and then it stops. An excerpt is a promise of the article, and a
	   card whose height depends on how much somebody typed is a card that breaks
	   the grid beside it. */
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
	color: var(--parch-dim);
	font-size: .92rem;
}

.lead-foot,
.brief-foot {
	display: flex;
	align-items: center;
	gap: 0 var(--s2);
	margin: 0;
	font-size: .76rem;
	color: var(--parch-faint);
}

.lead-dot { opacity: .5; }

/*
 * The four behind it, as rows.
 *
 * A recessed strip rather than four more cards: the lead is the card, and repeating
 * its treatment four times at a smaller size is what makes a news block look like a
 * template. These are a list, so they are drawn as one.
 */
.briefs {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	border: 1px solid var(--seam);
	background: var(--plate);
}

.briefs li + li { border-top: 1px solid var(--seam-soft); }

.brief {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--s4);
	padding: var(--s3) var(--s4);
	text-decoration: none;
	transition: background .15s;
}

.brief:hover { background: var(--forge); }

.brief-title {
	font-size: .92rem;
	font-weight: 500;
	color: var(--parch);
}

.brief-foot { flex: 0 0 auto; }

/* ── Tabs, without a line of JavaScript ───────────────── */

/*
 * Two radios, one name, `:checked` reveals its page.
 *
 * The panel already models its menu with a checkbox for the same reason: a tab
 * strip is one piece of state, CSS can hold one piece of state, and a ranking that
 * needs a script to appear is a ranking that is missing while the script loads. The
 * labels are focusable and toggle on space because that is what a radio does — the
 * accessible behaviour is inherited rather than rebuilt.
 */
/*
 * The tabbed panel is a column; only its strip is a row. Stated because the two used
 * to share the name `tabs` and the strip's `display: flex` flattened the panel.
 */
.panel--tabbed {
	display: grid;
	gap: var(--s4);
}

.tab-state {
	position: absolute;
	opacity: 0;
	pointer-events: none;
}

.tab-bar {
	display: flex;
	gap: var(--s1);
	border-bottom: 1px solid var(--seam);
}

.tab {
	padding: var(--s2) var(--s4);
	font: 600 .78rem/1 var(--ui);
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--parch-faint);
	cursor: pointer;
	border: 1px solid transparent;
	border-bottom: 0;
	/* The selected tab is marked *under* the word rather than behind it — a filled
	   tab competes with the gold button in the sidebar, and there should be one
	   filled thing on the page. */
	box-shadow: inset 0 -2px 0 transparent;
	transition: color .15s, box-shadow .15s;
}

.tab:hover { color: var(--parch); }

/* The pages are hidden by default and each is revealed by its own radio. */
.tab-page { display: none; }

#best-players:checked ~ .tab-bar [for='best-players'],
#best-clans:checked ~ .tab-bar [for='best-clans'] {
	color: var(--gold-hot);
	box-shadow: inset 0 -2px 0 var(--gold);
}

#best-players:checked ~ .tab-page--players,
#best-clans:checked ~ .tab-page--clans {
	display: block;
}

/* A keyboard user has to be able to see which tab has focus, and the radio itself
   is invisible — so the ring is drawn on its label. */
.tab-state:focus-visible ~ .tab-bar label[for] {
	outline: 2px solid var(--gold-hot);
	outline-offset: 2px;
}

/* ── A sidebar card ───────────────────────────────────── */

.card {
	display: grid;
	gap: var(--s2);
	padding: var(--s4);
}

.card-title {
	font-size: .9rem;
	letter-spacing: .08em;
	color: var(--parch-dim);
}

.card-main {
	margin: 0;
	font-size: 1.05rem;
	font-weight: 600;
	color: var(--parch);
}

.card-line {
	display: flex;
	align-items: center;
	gap: var(--s2);
	margin: 0;
	font-size: .85rem;
	color: var(--parch-dim);
}

.card-day { font-weight: 600; }
.card-clock { font-weight: 700; color: var(--gold); }

.card-count {
	margin: 0;
	font-size: .95rem;
	color: var(--parch);
}

.card-more {
	justify-self: start;
	margin-top: var(--s1);
	font: 600 .7rem/1 var(--ui);
	letter-spacing: .12em;
	text-transform: uppercase;
	color: var(--parch-faint);
	text-decoration: none;
}

.card-more:hover { color: var(--gold-hot); }

/*
 * The one filled control below the hero.
 *
 * Struck metal, like the masthead's own button, because it is the same action —
 * and there is exactly one of it on the page. A second gold button anywhere in this
 * column would halve the weight of both.
 */
.card-go {
	display: block;
	margin-top: var(--s2);
	padding: var(--s3);
	text-align: center;
	font: 700 .82rem/1 var(--ui);
	letter-spacing: .1em;
	text-transform: uppercase;
	text-decoration: none;
	color: var(--gold-ink);
	background: linear-gradient(180deg, var(--gold-hot), var(--gold) 45%, var(--gold-deep));
	border: 1px solid #00000073;
	box-shadow: inset 0 1px 0 #fff6, inset 0 -1px 0 #0006, 0 2px 0 #00000080;
	text-shadow: 0 1px 0 #ffffff40;
}

.card-go:hover { background: linear-gradient(180deg, #ffe9ab, var(--gold-hot) 45%, var(--gold)); }
.card-go:active { transform: translateY(1px); box-shadow: inset 0 1px 0 #fff4, 0 1px 0 #00000080; }

/* Two counts side by side. Label above figure, because the figure is what the eye
   is looking for and it should not have to pass the word to reach it. */
.tally {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: var(--s2);
	margin: var(--s2) 0 0;
	padding-top: var(--s3);
	border-top: 1px solid var(--seam-soft);
}

.tally div { display: grid; gap: 2px; }

.tally dt {
	font: 600 .64rem/1 var(--ui);
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--parch-faint);
}

.tally dd {
	margin: 0;
	font-size: 1.15rem;
	font-weight: 700;
	color: var(--parch);
}

/* ── The crowns, as rows ──────────────────────────────── */

.crowns {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: var(--s1);
}

.crown-row {
	display: grid;
	grid-template-columns: auto 1fr auto;
	align-items: center;
	gap: var(--s2);
	padding: var(--s2);
	background: var(--void);
	box-shadow: inset 0 1px 0 #00000099;
	/* The nation on the left edge, where the nation *is* the data. */
	border-left: 2px solid var(--side, var(--seam));
}

.crown-row.nation-karus { --side: var(--karus); }
.crown-row.nation-elmorad { --side: var(--elmorad); }

.crown-row .mark { width: 18px; height: 18px; image-rendering: pixelated; }

.crown-who { display: grid; gap: 1px; min-width: 0; }

.crown-nation {
	font: 600 .62rem/1 var(--ui);
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--side, var(--parch-faint));
}

.crown-name {
	font-size: .92rem;
	font-weight: 600;
	color: var(--parch);
	/* A twenty-character king does not push the tax rate off the card. */
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.crown-tax {
	font-size: .85rem;
	font-weight: 700;
	color: var(--parch-dim);
}

/*
 * The note under the stage, for a worn piece with no model.
 *
 * Deliberately quiet: it is a fact about the client's own files, not a failure the
 * reader can do anything about, and a warning-coloured banner would make an
 * ordinary gap in a fifteen-year-old game's art look like a broken page.
 */
.stage-note {
	margin: var(--s2) 0 0;
	font-size: .78rem;
	line-height: 1.5;
	color: var(--parch-faint);
}

/*
 * ── The jump icon ────────────────────────────────────────────────────────────
 *
 * Replaces the "Takvimin tamamı" / "Krallık" lines that sat at the foot of each
 * sidebar card. Those were a second reading line under a card built to be glanced
 * at, and they cost every card two centimetres of height for a label the icon
 * carries on its own.
 *
 * An arrow leaving its corner, drawn as three strokes rather than fetched: at 14px
 * an inline SVG is sharp at any pixel density and an icon font is a download.
 */
.card-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--s3);
}

.card-jump {
	flex: 0 0 auto;
	display: grid;
	place-items: center;
	width: 26px;
	height: 26px;
	color: var(--parch-faint);
	border: 1px solid var(--seam);
	background: var(--void);
	transition: color .15s, border-color .15s, background .15s;
}

.card-jump svg {
	width: 13px;
	height: 13px;
	fill: none;
	stroke: currentColor;
	stroke-width: 2;
	stroke-linecap: square;
}

.card-jump:hover {
	color: var(--gold-hot);
	border-color: var(--gold-deep);
	background: var(--forge);
}

/*
 * The panel header holds one too, and its heading has to stop growing to make
 * room: `.panel-title` is a plaque whose rule runs to the edge, so without this
 * the rule pushes the icon out of the row.
 */
.panel-head .card-jump { margin-left: var(--s2); }

/*
 * ═════════════════════════════════════════════════════════════════════════════
 *  THE CHARACTER SHEET, IN THE PLATE LANGUAGE
 * ═════════════════════════════════════════════════════════════════════════════
 *
 * The front page and the calendar were rebuilt on plates — hard corners, a bevel,
 * gold corner fittings — and this page was left behind on the old hairline-and-gap
 * treatment, which is the surest way to make one site look like two. So the same
 * device, applied to the four things on this page that are objects: the stage, the
 * national-points block, the equipment block and the stats.
 *
 * The **stage keeps its own frame** rather than becoming a plate. A plate's job is
 * to say "this is a thing"; the stage already says that with a metre of character
 * in it, and corner fittings over a 3D figure read as a picture frame around a
 * portrait — which is not what a live, turnable figure is.
 */

.stage {
	border-color: var(--seam);
	background: var(--void);
	box-shadow: var(--bevel);
	border-radius: 0;
}

/*
 * ── National points, the page's one big number ──────────────────────────────
 *
 * It was a label and a figure between two hairlines. Now it is the plate that
 * carries the standing beside it, because a number nobody can place is a number
 * nobody can read: 25,634,971 means nothing until it is also "1st of 3".
 */
.np {
	padding: var(--s4) var(--s5);
	border: 1px solid var(--seam);
	background:
		linear-gradient(180deg, var(--gold-veil), transparent 55%),
		var(--plate);
	box-shadow: var(--bevel);
	display: grid;
	gap: var(--s1);
}

.np-figure { margin: 0; }

/*
 * ── Where they stand ─────────────────────────────────────────────────────────
 *
 * Counted, not read: `USERDATA.Rank` is 0 on every row in this schema, so the
 * page's old "Nth in their nation" line was dead code against a column the server
 * never writes. See `CharacterRepository::standing`.
 */
.standing {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s1) var(--s4);
	margin: var(--s2) 0 0;
	padding-top: var(--s3);
	border-top: 1px solid var(--seam-soft);
	font-size: .82rem;
	color: var(--parch-dim);
}

.standing-place {
	font-family: var(--mono);
	font-weight: 700;
	color: var(--gold);
}

.standing-of { color: var(--parch-faint); }

/* ── The blocks ───────────────────────────────────────────── */

.block {
	padding: var(--s4) var(--s5);
	border: 1px solid var(--seam);
	background: var(--plate);
	box-shadow: var(--bevel);
	border-bottom: 1px solid var(--seam);
}

.block-title { margin-bottom: var(--s3); }

/* The fittings, on the two blocks that are containers rather than tables. */
.np,
.block {
	position: relative;
}

.np::before,
.block::before {
	content: '';
	position: absolute;
	inset: var(--fitting-inset);
	pointer-events: none;
	background:
		linear-gradient(var(--gold-deep), var(--gold-deep)) left  top    / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) left  top    / 1px var(--fitting) no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) right top    / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) right top    / 1px var(--fitting) no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) left  bottom / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) left  bottom / 1px var(--fitting) no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) right bottom / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) right bottom / 1px var(--fitting) no-repeat;
}

/* The sheet is a stack of plates now, so it needs the gap it never had. */
.sheet {
	display: grid;
	gap: var(--s4);
}

/*
 * ── Stats, with the allocation shown ────────────────────────────────────────
 *
 * Five bare numbers in five boxes told the reader what the values are and nothing
 * about what they *mean*. A stat is one byte in the game's own row, so 255 is not
 * a chosen ceiling but the width of the field — which makes a bar honest: it is
 * the share of the column that has been spent.
 *
 * The rebirth points sit on the same bar in a lighter tone rather than in a second
 * one. They are the same stat and adding them to the base would hide the only part
 * of this block a player chose twice, so they are stacked: base, then bonus.
 */
.stats {
	grid-template-columns: repeat(auto-fit, minmax(5.5rem, 1fr));
	gap: 1px;
	background: var(--seam-soft);
	border: 1px solid var(--seam-soft);
}

.stat {
	background: var(--void);
	padding: var(--s3) var(--s2) var(--s2);
	box-shadow: inset 0 1px 0 #00000080;
}

.stat dd {
	display: flex;
	align-items: baseline;
	justify-content: center;
	gap: .3em;
	margin: var(--s1) 0 var(--s2);
	font-weight: 700;
	color: var(--parch);
}

.stat-bonus {
	font-size: .72rem;
	font-weight: 500;
	color: var(--good);
}

/*
 * The bar. Two segments on one track: what the character started with and what a
 * rebirth added. `--base` and `--bonus` are percentages of 255, written inline, so
 * the figure above and the bar below cannot disagree.
 */
.stat-bar {
	display: flex;
	height: 3px;
	background: var(--seam-soft);
	overflow: hidden;
}

.stat-bar span:first-child {
	flex: 0 0 var(--base, 0%);
	background: linear-gradient(90deg, var(--gold-deep), var(--gold));
}

.stat-bar span:last-child {
	flex: 0 0 var(--bonus, 0%);
	background: var(--good);
}

/* Grows in on load, and by transform so the compositor does it. */
.stat-bar span {
	transform-origin: left center;
	animation: meter-grow .8s cubic-bezier(.22, 1, .36, 1) both;
}

@media (prefers-reduced-motion: reduce) {
	.stat-bar span { animation: none; }
}

/*
 * ── The record ───────────────────────────────────────────────────────────────
 *
 * Recessed cells, like the game's own list rows — and one figure added that the
 * page carried the halves of without ever doing the division: the kill/death
 * ratio. On a sicil of 50,307 and 15,965 it is the first number a reader looks
 * for, and it was a note under "PvP" in six-point grey.
 */
.record-cell {
	background: var(--void);
	box-shadow: inset 0 1px 0 #00000080;
	padding: var(--s3) var(--s2);
}

.record-cell--ratio .record-value { color: var(--gold); }

/*
 * ═════════════════════════════════════════════════════════════════════════════
 *  THE PLATE LANGUAGE, EVERYWHERE ELSE
 * ═════════════════════════════════════════════════════════════════════════════
 *
 * The front page and the character sheet were rebuilt on plates and the other
 * thirteen pages were left on the old hairline-and-gap treatment, which is the
 * surest way to make one site look like two. This closes that.
 *
 * It is one stylesheet pass rather than thirteen edits because the pages already
 * share their vocabulary — nine of them draw a `.ladder` of `.entry` rows under a
 * `.board`, and the rest are cards under a `.lede`. Naming the shared classes once
 * is what makes a design system a system instead of a look applied by hand.
 *
 * Three roles, and every container on the site is one of them:
 *
 *   **plaque**  a section — a heading and a rule, no box. `.board`, and the page
 *               headers. Framing these was the mistake the front page taught: a
 *               heading plus a list has no edges of its own, and giving it some
 *               produces a frame inside a frame around one row.
 *   **plate**   an object — something you could point at. A clan crest, a throne,
 *               a mirror on the download page. Hard corners, bevel, gold fittings.
 *   **recess**  a row in a list. Sunk into the page rather than standing on it,
 *               which is how the game draws a list and is most of why its windows
 *               read as carved rather than printed.
 */

/* ── The page header, as a plaque ──────────────────────────── */

/*
 * Every page but the front one opens with a `.lede`. It was a title, a note and a
 * hairline; now the title carries the plaque's rule and lozenge, so a page opens
 * in the same voice a section does.
 */
.lede {
	margin-bottom: var(--s6);
	padding-bottom: var(--s4);
	border-bottom: 1px solid var(--seam);
}

.lede-title {
	display: flex;
	align-items: center;
	gap: var(--s3);
	text-transform: uppercase;
	letter-spacing: .04em;
}

.lede-title::before {
	content: '';
	width: 6px;
	height: 6px;
	flex: 0 0 auto;
	background: var(--gold);
	transform: rotate(45deg);
}

.lede-title::after {
	content: '';
	flex: 1;
	height: 1px;
	background: linear-gradient(90deg, var(--gold-deep), transparent);
}

.lede-note {
	max-width: 56ch;
	color: var(--parch-dim);
}

/* ── Ladder rows, recessed ─────────────────────────────────── */

/*
 * `.entry` is the row on nine pages — every ranking, the clan list, a clan's
 * members, the crown's candidates, the world census, the gear boards, elite and
 * search. Recessing it here reaches all of them at once, and the inverted bevel is
 * what does it: dark at the top edge where a raised thing would be light.
 */
.entry {
	background: var(--void);
	box-shadow: inset 0 1px 0 #00000073;
	border-radius: 0;
}

/*
 * The first three keep a gold left edge. The top of a ladder is the reason anybody
 * opened it, and an edge marks a row without competing with the bar inside it.
 */
.entry:nth-child(-n+3) { border-left: 2px solid var(--gold-deep); }
.entry:nth-child(1) { border-left-color: var(--gold); }

.entry:hover { background: var(--forge); }

/* ── Cards, as plates ─────────────────────────────────────── */

/*
 * Every object on every remaining page, in one selector list. Long on purpose:
 * this is the inventory of what counts as a *thing* on this site, and a reader can
 * check it against the pages.
 */
.crest-card,
.crest,
.throne,
.curve-block,
.grab,
.spec,
.step,
.mirror,
.notice,
.channel,
.ask,
.lead,
.post,
.article {
	position: relative;
	padding: var(--s4);
	border: 1px solid var(--seam);
	border-radius: 0;
	background:
		linear-gradient(180deg, #ffffff0a, transparent 34%),
		var(--plate);
	box-shadow: var(--bevel);
}

/*
 * The fittings go on the larger ones only.
 *
 * A bracket is 11px long and sits 5px in, so on a card smaller than about 6rem the
 * four of them meet in the middle and read as a box drawn twice. `.figure` on the
 * crown page and `.race` on the world page are that small, so they get the plate
 * and not the corners — the restraint is what keeps the device legible where it is
 * used.
 */
.crest-card::before,
.crest::before,
.throne::before,
.curve-block::before,
.grab::before,
.spec::before,
.step::before,
.mirror::before,
.notice::before,
.channel::before,
.ask::before,
.lead::before,
.post::before,
.article::before {
	content: '';
	position: absolute;
	inset: var(--fitting-inset);
	pointer-events: none;
	background:
		linear-gradient(var(--gold-deep), var(--gold-deep)) left  top    / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) left  top    / 1px var(--fitting) no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) right top    / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) right top    / 1px var(--fitting) no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) left  bottom / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) left  bottom / 1px var(--fitting) no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) right bottom / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-deep), var(--gold-deep)) right bottom / 1px var(--fitting) no-repeat;
}

/* A card that is a link lights its fittings rather than lifting. */
.crest-card:hover::before,
.mirror:hover::before,
.post:hover::before,
.lead:hover::before {
	background:
		linear-gradient(var(--gold-hot), var(--gold-hot)) left  top    / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-hot), var(--gold-hot)) left  top    / 1px var(--fitting) no-repeat,
		linear-gradient(var(--gold-hot), var(--gold-hot)) right bottom / var(--fitting) 1px no-repeat,
		linear-gradient(var(--gold-hot), var(--gold-hot)) right bottom / 1px var(--fitting) no-repeat;
}

/*
 * The download page's steps are numbered, and here the numbering is not decoration
 * — installing a client *is* a sequence, so the numeral is information. It gets the
 * gold-on-dark treatment the rest of the site gives a rank.
 */
.step-no {
	color: var(--gold-ink);
	background: var(--gold);
	font-family: var(--mono);
	font-weight: 700;
}

/*
 * The world page's level curve. The bars were flat; a top-lit gradient makes them
 * read as filled columns rather than as coloured rectangles, and it is the same
 * fill the meter and the stat bars use — one bar treatment across the site.
 */
.curve-bar {
	background: linear-gradient(180deg, var(--gold), var(--gold-deep));
	border-radius: 0;
}

/* Nation shares stay in nation colours: there the nation is the data. */
.race-share { border-radius: 0; }

/*
 * The gear page's own tab strip predates the front page's and is a different
 * mechanism — links rather than radios, because those tabs are separate URLs with
 * their own data. They get the same *look* so a reader cannot tell that the two
 * are built differently, which is the only thing that matters here.
 */
.tabs {
	display: flex;
	gap: var(--s1);
	border-bottom: 1px solid var(--seam);
	margin-bottom: var(--s4);
}

.tabs .tab {
	padding: var(--s2) var(--s4);
	font: 600 .78rem/1 var(--ui);
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--parch-faint);
	text-decoration: none;
	box-shadow: inset 0 -2px 0 transparent;
	transition: color .15s, box-shadow .15s;
}

.tabs .tab:hover { color: var(--parch); }

.tabs .tab.is-here {
	color: var(--gold-hot);
	box-shadow: inset 0 -2px 0 var(--gold);
}

/*
 * An empty list says so on a plate rather than in the margin. "Kayıt yok" floating
 * in space reads as a page that failed to load; inside a frame it reads as an
 * answer.
 */
.empty {
	padding: var(--s5);
	border: 1px solid var(--seam);
	background: var(--plate);
	box-shadow: var(--bevel);
	color: var(--parch-faint);
	text-align: center;
}

/* The article body keeps its own measure — prose wants 65 characters, not a card. */
.article { padding: var(--s6); }
.prose { max-width: 68ch; }

/*
 * ── Two that are cells, not objects ──────────────────────────────────────────
 *
 * `.figure` on the crown page and `.race` on the world page were briefly given the
 * plate, and eighteen bevelled frames on one page is noise rather than structure.
 * They are **data cells** — the same role as a stat box or a record figure — so
 * they take the recess: sunk into the page, one hairline between them, nothing
 * around them.
 *
 * Getting this wrong is the same mistake as plating `.board`, one scale down: a
 * device applied to everything stops distinguishing anything.
 */
.figure,
.race {
	background: var(--void);
	box-shadow: inset 0 1px 0 #00000073;
	border-radius: 0;
	padding: var(--s3);
}

.figure-label,
.race-name {
	font: 600 .64rem/1 var(--ui);
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--parch-faint);
}

.figure-value {
	font-family: var(--mono);
	font-variant-numeric: tabular-nums;
	font-weight: 700;
	color: var(--parch);
}

/*
 * ── The emblem in a ladder row ───────────────────────────────────────────────
 *
 * A fourth column, between the rank and the name. The row was `2.4rem 1fr auto`;
 * the crest needs its own track or it pushes the name and every row's name starts at
 * a different place depending on whether its emblem loaded.
 *
 * `image-rendering: pixelated` because these are the game's own 2004 emblems: scaled
 * with interpolation they are grey smudges, left hard they read as pixel art, which
 * on a site about a game of this vintage is the correct register.
 */
/*
 * The emblem column is a **fixed 22px**, not `auto`.
 *
 * `auto` sizes to the content, so a crest arriving late moved every name on the page
 * — and the emblems are not square once trimmed to their own bounds (Karus is 40×30,
 * El Morad 32×31), which an `auto` track would let vary row to row. A fixed track
 * with the image contained inside it gives one left edge for every name and no shift.
 */
.entry {
	grid-template-columns: 2.4rem 30px minmax(0, 1fr) auto;
}

/*
 * 20px on 16px art, and that is deliberate.
 *
 * `class_0..9` is a **fully opaque** 16px tile — a glyph on a nation-coloured square —
 * so scaling it up by an exact factor with `pixelated` gives clean hard pixels rather
 * than a blur. That is why it works where the nation crests did not: those were sparse
 * art on an empty canvas and being *reduced*, which nothing fixes.
 *
 * 20 rather than 32 because a ladder row is 2rem tall and the icon is beside a name,
 * not instead of it.
 */
.entry .mark {
	/*
	 * **The frame carries the size, not the bitmap.**
	 *
	 * This icon has now been 16px, 20px, 22px and 32px, and every one of those was
	 * wrong for the same reason: they all tried to make a 16×16 bitmap *bigger*. It
	 * cannot get bigger. At 20 and 22 the scale is fractional and the art goes soft;
	 * at 32 the scale is a clean 2× and the art goes *chunky* — each pixel becomes a
	 * visible 2×2 block, which is the pixellation being reported now. `pixelated`
	 * chooses between blurry and blocky. It cannot add detail that was never drawn.
	 *
	 * So the bitmap stops being scaled at all. It sits at its native 16px inside a
	 * 32px recessed tile: `padding` shrinks the content box to exactly the art's own
	 * size, so every pixel maps 1:1 to a screen pixel and the icon is as sharp as the
	 * client draws it. The *cell* is what grew — and it is the cell a reader sees as
	 * "big", because a framed 16px glyph reads as an object while a bare one reads as
	 * a speck. This is how the game does it too: its own windows put these marks in a
	 * sunken square rather than blowing them up.
	 */
	box-sizing: border-box;
	width: 30px;
	height: 30px;
	padding: 7px;
	object-fit: none;
	image-rendering: pixelated;
	background: var(--void);
	box-shadow:
		inset 0 1px 0 #00000099,
		inset 0 0 0 1px var(--seam-soft);
}

/*
 * The grade badge is 64px art and the only mark here that can be *reduced*, which is
 * the one direction that stays clean. It fills its tile rather than sitting in one,
 * and it smooths on the way down — nearest-neighbour halving would throw away every
 * other pixel of a numeral.
 */
.entry .mark--grade {
	padding: 2px;
	object-fit: contain;
	image-rendering: auto;
}

.entry:hover .mark { opacity: 1; }

/*
 * A row with no emblem — a clan with no grade, or a board with no nation — keeps the
 * column so the names still line up. An empty track is cheaper than a placeholder
 * image and says nothing false.
 */
/* A row with no emblem keeps the empty track, so names still line up down the page.
   An empty 22px is cheaper than a placeholder and says nothing false. */

/* On a phone the emblem goes: 20px of crest against a 40-character name is the
   wrong trade, and the bar's colour still carries the nation. */
@media (max-width: 34rem) {
	/* On a phone the icon stays — at 32px it is the most legible thing in the row —
	   but the track tightens and the rank narrows to pay for it. */
	.entry { grid-template-columns: 1.8rem 24px minmax(0, 1fr) auto; }
	/* Tighter tile, same untouched 16px inside it. */
	.entry .mark { width: 24px; height: 24px; padding: 4px; }
}

/* The grade badge is a numeral and reads at a size a crest cannot. */


/*
 * ── A clan row's two marks ───────────────────────────────────────────────────
 *
 * The game's own Clan Ranking window puts the grade badge and the clan's uploaded
 * emblem side by side, both small. Copied because it is right: the badge says the
 * rank and the emblem says who, and neither substitutes for the other.
 *
 * The track widens to hold both, and a clan with only one of them still lines its
 * name up with every other row.
 */
.entry-crest {
	display: flex;
	align-items: center;
	gap: 3px;
}



/* The emblem is a player's own 32px upload, so it is shown under its own size and
   never smoothed — a clan mark is pixel art whether its author meant it or not. */
.entry-emblem {
	/* The clan's own upload is 32px art, so 32 is its native size — no scale at all. */
	width: 30px;
	height: 30px;
	object-fit: contain;
	image-rendering: pixelated;
}

.entry:has(.entry-crest) { grid-template-columns: 2.4rem auto minmax(0, 1fr) auto; }

/*
 * ── Nation crest and class mark, side by side ───────────────────────────────
 *
 * Two marks where the row's template counts one track, so the track sizes itself
 * — the same fix the clan crest needed, and for the same reason: a fixed width
 * here squeezes the pair and pushes the name into the emblem's column.
 */
.entry:has(.entry-marks) { grid-template-columns: 2.4rem auto minmax(0, 1fr) auto; }

.entry-marks {
	display: flex;
	align-items: center;
	gap: 4px;
}

/*
 * The crest is trimmed to its own bounds, so it is 40×30 rather than square. Only
 * the height is set; the width follows, which is what keeps it from squashing.
 */
.entry-marks .mark--nation {
	width: auto;
	height: 22px;
	padding: 0;
	background: none;
	box-shadow: none;
}

/* The row grows to hold a 32px icon without squeezing the two lines of text. */
.entry { padding-block: var(--s2); }

/* The clans page keeps its own row markup; its mark slot holds the same pair. */
/*
 * ── The clan row's mark pair ─────────────────────────────────────────────────
 *
 * Grade badge and emblem, and **no rank numeral beside them**: a grade badge is a
 * Roman numeral, so a `1` printed next to a `I` reads as the same fact written
 * twice — which is exactly how it looked. The clan ladder keeps its order from the
 * list and its leaders from the gold left edge, and the numeral it does show is the
 * one that is not derivable from either.
 */
/*
 * **Three columns, because the row now has three children.** Dropping the rank
 * numeral left a four-track template counting one track too many, so the mark pair
 * was squeezed into a 30px cell and the emblem spilled over the clan's name. The
 * pair sizes itself (`auto`) since a clan may carry a badge, an emblem, both or
 * neither.
 */
.entry.is-clan { grid-template-columns: auto minmax(0, 1fr) auto; }

.entry-mark {
	display: flex;
	align-items: center;
	gap: var(--s1);
}

/*
 * ── The show/hide box, above its square ──────────────────────────────────────
 *
 * Where the game puts it: a small tick sitting on the square's top edge, in the
 * gap above the row rather than on the icon. That is why the costume grid gets a
 * little more room between rows than the worn one — the boxes live in it.
 */
.doll:has(.cell-toggle) {
	gap: 12px 4px;
	/* Room for the first row's ticks, which overlap the top of their squares. */
	padding-top: 8px;
}

.cell-toggle {
	position: absolute;
	right: 1px;
	/*
	 * Overlapping the square's top edge rather than floating clear of it: the game
	 * sets its own boxes half over the plate, which is what ties the tick to the
	 * square it belongs to instead of leaving it adrift between two rows.
	 */
	bottom: calc(100% - 5px);
	z-index: 3;
	display: grid;
	place-items: center;
	cursor: pointer;
}

.cell-toggle input {
	width: 11px;
	height: 11px;
	margin: 0;
	accent-color: var(--gold);
	cursor: pointer;
}

/* A hidden piece: the square says so, so the doll and the figure agree. */
.cell:has(.cell-toggle input:not(:checked)) { opacity: .45; }

/* The hole in the costume grid's third row: a plate with nothing in it. */
.cell.is-void {
	pointer-events: none;
	opacity: .35;
}

/*
 * ── The hover frame: a light that travels the border ─────────────────────────
 *
 * The shape is the client's own — `iconselect.dxt`, a thin square frame — used
 * here as a **mask** rather than as an image. Nothing is rotated: turning the
 * element swung its corners out past the plate and read as a spinning diamond
 * rather than as a band following the edge, which is the opposite of the effect.
 *
 * What turns instead is a conic gradient *inside* the mask. The frame stays
 * exactly where it is and a bright arc sweeps around it, because only the paint
 * moves. `@property` is what makes the angle animatable at all — a plain custom
 * property is a string to the compositor and would jump rather than sweep.
 */
@property --beam {
	syntax: '<angle>';
	initial-value: 0deg;
	inherits: false;
}

.cell-frame {
	position: absolute;
	inset: 1px;
	z-index: 2;
	pointer-events: none;
	opacity: 0;
	transition: opacity .12s;

	/* The frame's own colour, plus a brighter arc riding over it. */
	background:
		conic-gradient(
			from var(--beam),
			transparent 0deg 200deg,
			rgb(200 255 255 / .95) 290deg,
			transparent 340deg
		),
		rgb(160 224 224 / .5);

	mask: url('/img/game/iconselect.png') center / 100% 100% no-repeat;
	-webkit-mask: url('/img/game/iconselect.png') center / 100% 100% no-repeat;
}

.cell:hover .cell-frame,
.cell:focus-visible .cell-frame {
	opacity: 1;
	animation: cell-frame-beam 1.8s linear infinite;
}

@keyframes cell-frame-beam {
	from { --beam: 0deg; }
	to   { --beam: 360deg; }
}

@media (prefers-reduced-motion: reduce) {
	.cell:hover .cell-frame,
	.cell:focus-visible .cell-frame { animation: none; }
}
