*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
/* Whatever a rule below sets `display` to, an element the page has hidden stays hidden. */
[hidden] { display: none !important; }

/*
 * Self-hosted rather than linked from Google. The site is served from one origin that also
 * serves the installer everyone is here for, and a display face that fails to arrive changes
 * every heading on the page — a dependency worth 57 KB not to have. Both files are the latin
 * subset of the variable font, so one file covers the whole weight range each face uses;
 * `FONT_FILES` below copies them into the build.
 */
@font-face {
  font-family: 'Cinzel';
  src: url('/fonts/cinzel-latin.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'JetBrains Mono';
  src: url('/fonts/jetbrains-mono-latin.woff2') format('woff2');
  font-weight: 400 500;
  font-style: normal;
  font-display: swap;
}

:root {
  /*
   * Worn stone, not a void. The key art is a bright valley under a blue sky and the page used
   * to sit it on #0a0a0a, which reads as a hole cut in the screen rather than a window onto
   * the world. These are the colours of the game's own frame — the stone border it draws over
   * every screen — so the site and the thing it advertises are lit the same way.
   */
  --bg:        #2b2723;   /* the wall behind everything */
  --bg2:       #211d19;   /* recessed: the nav bar, table headers */
  --panel:     #383128;   /* a lit stone plate — the page's own window */
  --panel2:    #423a2e;   /* a card resting inside that window */
  --border:    #554c3c;
  --border2:   #6c6049;   /* the lit edge of a bevel */
  --carve:     #1d1a16;   /* the dark line cut in behind it */
  --gold:      #d2a83a;
  --gold-lit:  #f0e2c0;
  --text:      #e2d8c3;
  --dim:       #a2947a;
  --good:      #5ac45a;
  --bad:       #c45a5a;

  /* Cinzel cuts the headings; JetBrains Mono keeps the body in the game's own voice. */
  --f-display: "Cinzel", "Palatino Linotype", Georgia, serif;
  --f-body:    "JetBrains Mono", ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace;

  /* The sticky nav's height. The wiki's own bars stick underneath it and need to know. */
  --nav-h:     54px;
}

html { scroll-behavior: smooth; }

/*
 * Coursed masonry, plus a wash of warm light off the top.
 *
 * One 116×116 tile holding two courses: a joint at the left of the upper block and one at the
 * middle of the lower, so each course is laid half a block over the one under it. Crossed
 * repeating-linear-gradients cannot do that — a vertical line from one runs the full height of
 * the page and the wall comes out as graph paper, which is what this first shipped as. The
 * texture is deliberately near the threshold of visible: the job is to stop the ground reading
 * as flat, not to be something anyone looks at.
 */
body {
  background:
    radial-gradient(130% 100% at 50% -10%, rgba(255, 236, 196, 0.055), transparent 62%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='116' height='116'%3E%3Cg fill='none' stroke='%23000'%3E%3Cpath d='M0 .5h116M0 58.5h116' stroke-opacity='.24'/%3E%3Cpath d='M.5 0v58M58.5 58v58' stroke-opacity='.15'/%3E%3C/g%3E%3C/svg%3E") repeat,
    var(--bg);
  color: var(--text);
  font-family: var(--f-body);
  font-size: 15px;
  line-height: 1.65;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
/*
 * The vignette, held to the window rather than to the document — on a long page a gradient in
 * the stack above would put its dark edge at the bottom of the *page*, which is nowhere the
 * reader ever is. Negative z-index drops it behind every in-flow element but still over the
 * canvas, which is where body's own background ends up, so no content needs a stacking context
 * of its own to stay above it. Out of flow, so body's flex column never sees it.
 */
body::after {
  content: '';
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(120% 85% at 50% 38%, transparent 42%, rgba(0, 0, 0, 0.46) 100%);
}

a { color: var(--gold); }
a:hover { color: var(--gold-lit); }

:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
::selection { background: rgba(210, 168, 58, 0.30); color: var(--gold-lit); }

/* ── Top bar ── */
/*
 * A floor rather than a height. Should the nav ever need a second row above the width where
 * the bar stacks, the bar grows to hold it instead of cutting the first row off the screen.
 */
.site-nav {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; gap: 28px;
  padding: 0 28px; min-height: var(--nav-h);
  background: linear-gradient(180deg, #2f2a25, #1c1915);
  border-bottom: 2px solid var(--border2);
  box-shadow: inset 0 1px 0 rgba(255, 235, 190, 0.07), 0 3px 14px rgba(0, 0, 0, 0.45);
}
.site-nav .brand {
  font-family: var(--f-display);
  font-size: 17px; letter-spacing: 5px; text-transform: uppercase;
  color: var(--gold); text-decoration: none; font-weight: 700; flex-shrink: 0;
}
.site-nav nav { display: flex; gap: 22px; flex-wrap: wrap; }
.site-nav nav a {
  color: var(--dim); text-decoration: none; font-size: 13px;
  letter-spacing: 1px; text-transform: uppercase;
  padding: 4px 0; border-bottom: 2px solid transparent;
}
.site-nav nav a:hover { color: var(--gold); }
.site-nav nav a.active { color: var(--gold); border-bottom-color: var(--border2); }
/*
 * How many are playing, at the far end of the bar on every page. Hidden until the server answers
 * (LIVE_SCRIPT), so a page whose server is down or older than the route shows nothing rather than
 * a wrong number; the status light on the front and download pages is what says it is down.
 */
.site-nav .online {
  margin-left: auto; flex-shrink: 0; white-space: nowrap;
  color: var(--dim); text-decoration: none; font-size: 12px;
  letter-spacing: 1px; text-transform: uppercase;
}
.site-nav .online:hover { color: var(--gold); }
.site-nav .online .dotlight {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 7px;
  vertical-align: middle; background: var(--good); box-shadow: 0 0 6px var(--good);
}
/*
 * Too narrow for the brand and the nav on one row, so the bar stacks. The width is worked out
 * from the nav's own labels (NAV_STACK_PX), and the wiki unpins its sticky bars at the same
 * one, since no fixed offset can follow a bar that has grown.
 */
@media (max-width: 1283px) {
  .site-nav { padding: 10px 16px; flex-direction: column; align-items: flex-start; gap: 10px; }
  /* Beside the brand, on the row the brand has to itself, rather than a third row of its own. */
  .site-nav .online { position: absolute; top: 10px; right: 16px; line-height: 28px; }
}

main { flex: 1; width: 100%; max-width: 960px; margin: 0 auto; padding: 46px 24px 72px; }
/*
 * The page's content is a carved window, not text lying on the floor: a bevelled stone plate
 * with the dark line of the cut behind its lit edge, a gold hairline inlaid inside it, and a
 * hard shadow directly under it so it sits proud of the wall.
 *
 * The width is `min()` rather than `max-width` because this box has a visible border now —
 * a plate that reaches the screen edge on a narrow window has no frame on two of its sides.
 * It keeps `flex: 1`, so on a short page the window grows to meet the footer instead of
 * leaving a strip of bare wall under it.
 */
main.under-band {
  position: relative;
  width: min(960px, calc(100% - 32px));
  max-width: none;
  margin: 32px auto 44px;
  padding: 30px 34px 40px;
  background: var(--panel);
  border: 2px solid var(--border2);
  box-shadow: inset 0 0 0 1px var(--carve),
              inset 0 2px 0 rgba(255, 235, 190, 0.10),
              0 5px 0 rgba(0, 0, 0, 0.32),
              0 16px 34px rgba(0, 0, 0, 0.34);
}
main.under-band::before {
  content: ''; position: absolute; inset: 7px; pointer-events: none;
  border: 1px solid rgba(210, 168, 58, 0.22);
}
/* The wiki brings its own width and padding; main must not second-guess either. */
main.wide { max-width: none; padding: 0; }
main.bare {
  display: flex; align-items: center; justify-content: center;
  text-align: center; min-height: 100vh;
  /* Overrides main's 960px cap: a boxed background leaves black bars either side of it. */
  max-width: none; padding: 24px;
  background: var(--bg) url('/img/site-banner.webp') center / cover no-repeat;
  position: relative;
}
/* Enough of a scrim that gold-on-blue never has to be read. */
main.bare::before { content: ''; position: absolute; inset: 0; background: rgba(43, 39, 35, 0.86); }
main.bare > * { position: relative; }

/* ── Key art ── */
/* The login screen's picture. Full-bleed, so these live outside <main>. */
/*
 * The crest is its own layer, and the scene under it is a picture shaped for a band.
 *
 * This was one image once — the game's login art, 3:2, with the wordmark painted into its top
 * third — anchored to the top so a cover crop could not slice the crest off. It cost the
 * picture: against a band nearer 3.5:1 that crop showed 42% of the art's height at 1440px and
 * 30% at 2560px, all of it the sky the wordmark sits in.
 *
 * Two things fixed it and both still matter. The crest became its own layer, drawn from
 * logo.webp — the mark alone on transparency — so it goes where it reads rather than where it
 * was painted; and the scene became the site's own banner at 2000x750, which is 2.67:1 and so
 * loses far less to the crop: 74% of its height at 1440px and 54% at 2560px, centred, which
 * holds the mountains, the castle, the town and the river at once.
 *
 * Narrower than about 610px the crop turns over and the band takes the art's full height,
 * cropping the sides instead — which is why one framing serves a phone and a 4K monitor
 * without a media query.
 *
 * The crest is sized off the band's height rather than its width, so it keeps its footing as
 * the band grows. Its canvas carries about a third of its height in transparent padding, which
 * is why 68% of the band draws a mark around half of it.
 */
.hero-art {
  position: relative;
  height: clamp(230px, 40vw, 400px);
  background:
    url('/assets/misc/logo.webp') center 44% / auto 68% no-repeat,
    var(--bg) url('/img/site-banner.webp') center 50% / cover no-repeat;
}
/* The wider the viewport, the less of the art's height a cover crop shows — 74% at 1440px
   against 54% here — so the band grows to keep the valley in frame. */
@media (min-width: 1900px) {
  .hero-art { height: clamp(400px, 21vw, 520px); }
}
/* Melts the art into the page instead of ending it on a hard horizontal line. */
.hero-art::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(to bottom,
              rgba(43, 39, 35, 0.06) 0%,
              rgba(43, 39, 35, 0.00) 40%,
              rgba(43, 39, 35, 0.70) 84%,
              var(--bg) 100%);
}

/*
 * The heading band. Its scrim used to run 78%–93% opaque, which left the art as a rumour —
 * a picture worth commissioning and then almost entirely painted over. It runs 42%–90% now,
 * and the heading no longer needs the whole frame darkened because it carries its own stone
 * plate: the same window the content below it sits in, laid over the valley.
 */
.band {
  position: relative;
  /* Centred, where the old 3:2 art wanted 42% to reach its horizon: this banner is 2.67:1,
     so the middle of it already is the horizon, the castle and the river. */
  background: var(--bg) url('/img/site-banner.webp') center / cover no-repeat;
  border-bottom: 2px solid var(--border2);
}
.band::before {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(to bottom, rgba(43, 39, 35, 0.42), rgba(43, 39, 35, 0.90));
}
.band-in {
  position: relative;
  width: min(960px, calc(100% - 32px));
  /* Generous above and below, because this margin is the only place the picture shows —
     the plate is as wide as the content window under it so the two edges line up. */
  margin: 46px auto;
  padding: 22px 28px;
  background: rgba(33, 29, 25, 0.86);
  border: 2px solid var(--border2);
  box-shadow: inset 0 0 0 1px var(--carve),
              inset 0 2px 0 rgba(255, 235, 190, 0.10),
              0 10px 26px rgba(0, 0, 0, 0.38);
}
/* A wide page's content runs the full viewport, and a heading left in the 960px reading
   column would sit indented from the bars directly beneath it. */
.band-in.wide { width: auto; margin: 30px 24px; }
.band h1 { margin-bottom: 6px; }
.band .lede { margin-bottom: 0; }
/* A class page's band: the class's emblem in a ring of its colour, its name lit in it, and the
   weapons it holds over its sentence (`band`'s `crest`). The colour arrives as --crest;
   everything else is mixed from it, so the six bands need no table of tones. */
.band-in.crested {
  display: flex; align-items: center; gap: 26px;
  border-color: var(--crest);
  box-shadow: inset 0 0 0 1px var(--carve),
              inset 0 0 0 2px color-mix(in srgb, var(--crest) 30%, transparent),
              0 0 40px color-mix(in srgb, var(--crest) 16%, transparent),
              0 10px 26px rgba(0, 0, 0, 0.38);
}
.crest-mark {
  flex: none; display: flex; align-items: center; justify-content: center;
  width: 92px; height: 92px; border-radius: 50%; font-size: 52px;
  color: color-mix(in srgb, var(--crest) 50%, #fff);
  background: radial-gradient(circle at 50% 38%, color-mix(in srgb, var(--crest) 32%, #12101c), #12101c);
  border: 3px solid var(--crest);
  box-shadow: 0 0 0 4px var(--carve), 0 0 0 5px color-mix(in srgb, var(--crest) 55%, transparent),
              0 0 28px color-mix(in srgb, var(--crest) 40%, transparent);
}
.crest-mark svg { display: block; }
.crest-text { min-width: 0; }
.band-in.crested h1 { color: color-mix(in srgb, var(--crest) 45%, #fff); margin-bottom: 2px; }
.crest-kicker {
  margin-bottom: 10px; font-size: 12px; letter-spacing: 2px; text-transform: uppercase;
  color: color-mix(in srgb, var(--crest) 75%, #fff);
}

/* Visible to screen readers only — the wordmark lives in the hero art, so the page still
   needs a real <h1> for anyone not looking at a picture. */
.vh {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── Type ── */
/* Cinzel is cut for capitals, so the headings carry the letter-spacing and the body face
   never has to pretend to be a display face. */
h1, h2, h3 { font-family: var(--f-display); font-weight: 700; text-wrap: balance; }
h1 { font-size: 34px; letter-spacing: 2px; color: var(--gold-lit); margin-bottom: 10px; }
h2 {
  font-size: 21px; letter-spacing: 1px; color: var(--gold);
  margin: 44px 0 14px; padding-bottom: 8px; border-bottom: 1px solid var(--border);
}
h3 { font-size: 16px; color: var(--gold-lit); margin: 24px 0 8px; letter-spacing: 1px; }
p { margin-bottom: 14px; }
.lede { color: var(--dim); font-size: 15px; margin-bottom: 32px; }
ul, ol { margin: 0 0 16px 22px; }
li { margin-bottom: 7px; }
code {
  background: rgba(0, 0, 0, 0.24); border: 1px solid var(--border);
  padding: 1px 6px; font-size: 13px; color: var(--gold-lit);
}

/* ── Panels ── */
/* A card resting inside the window, not a second carved frame. The page already has one
   heavy edge; repeating it here would flatten the two into a single busy texture. */
.panel {
  background: var(--panel2); border: 1px solid var(--border);
  box-shadow: inset 0 1px 0 rgba(255, 235, 190, 0.06);
  padding: 22px 26px; margin-bottom: 20px;
}

/* ── Hero ── */
.hero { text-align: center; padding: 4px 0 12px; }
.hero img.logo { width: 190px; max-width: 60%; image-rendering: pixelated; margin-bottom: 6px; }
.hero h1 { font-size: 46px; letter-spacing: 7px; margin-bottom: 4px; }
.hero .tagline { color: var(--dim); font-size: 15px; letter-spacing: 2px; margin-bottom: 30px; }

/* Cast metal on stone: a lit top edge, and a hard shadow it drops onto when pressed. */
.dl {
  display: inline-block; text-decoration: none;
  font-family: var(--f-display); font-weight: 700;
  padding: 16px 44px; font-size: 17px; letter-spacing: 2px;
  background: linear-gradient(180deg, #6d5824, #463814);
  color: #f5e6bb; border: 2px solid #8a6f2e;
  box-shadow: inset 0 1px 0 rgba(255, 235, 190, 0.20), 0 3px 0 rgba(0, 0, 0, 0.38);
}
.dl:hover { background: linear-gradient(180deg, #806829, #55441a); color: #fff3d0; }
.dl:active {
  transform: translateY(2px);
  box-shadow: inset 0 1px 0 rgba(255, 235, 190, 0.20), 0 1px 0 rgba(0, 0, 0, 0.38);
}
.dl-meta { color: var(--dim); font-size: 12px; margin-top: 13px; }

/* ── Server status ── */
.status { margin-top: 22px; font-size: 12px; color: var(--dim); }
.status .dotlight {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: var(--dim); margin-right: 7px; vertical-align: middle;
}
.status.up   .dotlight { background: var(--good); box-shadow: 0 0 6px var(--good); }
.status.down .dotlight { background: var(--bad); }

/* ── The world so far (the Statistics page) ── */
.world-totals { scroll-margin-top: calc(var(--nav-h) + 18px); }
.totals { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 10px; }
.total {
  background: var(--panel2); border: 1px solid var(--border);
  box-shadow: inset 0 1px 0 rgba(255, 235, 190, 0.06);
  padding: 16px 12px; text-align: center;
}
.total b {
  display: block; font-family: var(--f-display); font-size: 26px; letter-spacing: 1px;
  color: var(--gold-lit);
}
.total span {
  display: block; color: var(--dim); font-size: 12px; letter-spacing: 1px; text-transform: uppercase;
  text-wrap: balance;
}
.totals-note { color: var(--dim); font-size: 12px; }
.totals-row { margin: 22px 0 8px; }
/*
 * Four to a row until "Accounts registered", the longest label, would wrap in its tile (measured:
 * below about 870px), then two. On the narrowest phones a label may still take two lines; the
 * rows keep their tiles one height, and the break is balanced rather than leaving a lone word.
 */
@media (max-width: 880px) { .totals { grid-template-columns: repeat(2, 1fr); } }
/*
 * Characters created, by class: one measure across six categories, so one hue — the site's gold —
 * with the bars growing from a hairline baseline and the count at each tip. The class's own colour
 * is on its emblem beside the name, where it says which class this is, and never on the text.
 */
.class-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.class-head span { color: var(--dim); font-size: 12px; }
.class-bars { display: grid; gap: 6px; margin: 4px 0 18px; }
.class-bar { display: grid; grid-template-columns: 8.5em 1fr; align-items: center; gap: 12px; }
.cb-name {
  display: flex; align-items: center; gap: 8px; white-space: nowrap;
  color: var(--text); font-size: 13px; letter-spacing: 1px; text-transform: uppercase;
}
.cb-icon { display: inline-flex; color: var(--cc); font-size: 17px; }
.cb-track { display: flex; align-items: center; gap: 8px; min-height: 24px; border-left: 1px solid var(--border2); }
.cb-fill {
  height: 14px; width: calc((100% - 6em) * var(--r, 0)); background: var(--gold);
  border-radius: 0 4px 4px 0; transition: width 0.5s ease;
}
.cb-value { color: var(--dim); font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap; }
@media (max-width: 560px) { .class-bar { grid-template-columns: 7.2em 1fr; gap: 8px; } }
/* Deaths by boss: the same bars, in the deaths' red, with room for a boss's longer name. */
.class-bar.boss-bar { grid-template-columns: 12em 1fr; }
.boss-bar .cb-fill { background: var(--bad); }
@media (max-width: 560px) { .class-bar.boss-bar { grid-template-columns: 9em 1fr; } }

/* ── Feature grid ── */
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 16px; }
.grid .panel { margin-bottom: 0; }
.grid h3 { margin-top: 0; }
.grid p { color: var(--dim); font-size: 13px; margin-bottom: 0; }

/* ── Tables ── */
table { width: 100%; border-collapse: collapse; margin-bottom: 20px; font-size: 14px; }
th, td { text-align: left; padding: 9px 12px; border-bottom: 1px solid var(--border); }
th { color: var(--gold); background: rgba(0, 0, 0, 0.26); font-size: 12px;
     letter-spacing: 1px; text-transform: uppercase; }
tr:nth-child(even) td { background: rgba(0, 0, 0, 0.14); }
td.k { width: 190px; white-space: nowrap; }

/* ── Patch notes ── */
.entry { margin-bottom: 52px; }
.entry-head { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
              border-bottom: 1px solid var(--border); padding-bottom: 10px; margin-bottom: 6px; }
.entry-head h2 { border: 0; margin: 0; padding: 0; font-size: 22px; }
.tag {
  background: #55441a; border: 1px solid #8a6f2e; color: var(--gold-lit);
  padding: 2px 10px; font-size: 12px; letter-spacing: 1px;
}
.tag.dev { background: transparent; color: var(--dim); border-color: var(--border); }
.entry .when { color: var(--dim); font-size: 12px; }
.entry .summary { color: var(--dim); margin: 12px 0 4px; }
/* A category is the page's own h3; a subject sits under it a step down in the same face, so
   the pair reads as a heading and its sub-heading rather than as two headings side by side. */
.entry h4 {
  font-family: var(--f-display); font-weight: 700; font-size: 13px; letter-spacing: 1px;
  color: var(--gold); margin: 14px 0 6px;
}
.entry h3 + h4 { margin-top: 6px; }
/* The nav sticks, so a link to an entry would otherwise land its heading underneath it. */
.entry[id] { scroll-margin-top: calc(var(--nav-h) + 18px); }

/* ── Callout ── */
.note {
  border-left: 3px solid var(--gold); background: rgba(0, 0, 0, 0.22);
  padding: 16px 20px; margin: 22px 0; font-size: 14px;
}
.note strong { color: var(--gold); }

/* ── Donate ── */
/*
 * The button is PayPal's own image, served from PayPal, so the page frames it rather than
 * restyling it: centred on the window with room around it, the way the download button sits
 * on its page. The form is a flex row so the button is a flex item rather than an image on a
 * line of text, which would sit on the baseline with a descender's gap under it.
 */
.donate { text-align: center; padding: 4px 0 8px; }
.donate form { display: flex; justify-content: center; margin: 10px 0 4px; }
.donate input[type="image"] { cursor: pointer; }

/* ── Gameplay ── */
/*
 * The trailer, above everything else on the page.
 *
 * `preload="none"` with a poster, so a visitor who came to read about the classes pays for
 * one still and nothing else. The file is about 20 MB — several times the weight of every other
 * page on the site put together — and spending that before anybody has asked to watch it is
 * the one thing a video on a page like this gets wrong.
 *
 * It carries a border where `.panel` deliberately does not, because the two are doing
 * different jobs: a panel is a card resting on the page's own window, and a video with no
 * edge reads as a hole cut in it.
 */
.trailer { margin: 0 0 26px; border: 1px solid var(--border); background: var(--carve); }
.trailer video {
  display: block; width: 100%; height: auto; background: var(--carve);
  /* The element is zero-height until the poster arrives, so without this the page reflows
     under the reader the moment it does — the width/height attributes say the same thing to
     a browser that derives the ratio from them, and this covers the ones that do not. */
  aspect-ratio: 16 / 9;
}
.trailer figcaption {
  padding: 8px 14px; border-top: 1px solid var(--border);
  font-size: 12px; letter-spacing: 1px; color: var(--dim);
}
.jump { display: flex; flex-wrap: wrap; gap: 8px; }
.jump a {
  text-decoration: none; font-size: 12px; letter-spacing: 1px; text-transform: uppercase;
  color: var(--dim); border: 1px solid var(--border); background: rgba(0, 0, 0, 0.18);
  padding: 5px 12px;
}
.jump a:hover { color: var(--gold); border-color: var(--border2); }
/* The nav sticks, so an anchor jump would otherwise land its heading underneath it. */
h2[id] { scroll-margin-top: calc(var(--nav-h) + 18px); }
.more { margin-top: 14px; }

/*
 * One card per class, in the class's own colour — the same `CLASS_COLORS` entry the game
 * paints its nameplates and hub cards in, handed down as --cc. Two columns, and the two specs
 * stack inside a card rather than sitting side by side, which at this width would wrap each
 * blurb to a handful of words a line.
 */
.class-grid {
  display: grid; gap: 18px; margin: 18px 0 6px;
  grid-template-columns: repeat(auto-fit, minmax(min(380px, 100%), 1fr));
}
.class-card {
  position: relative; display: flex; flex-direction: column;
  background: var(--panel2); border: 1px solid var(--border); border-top: 3px solid var(--cc);
  box-shadow: inset 0 1px 0 rgba(255, 235, 190, 0.06);
  padding: 18px 20px 20px;
}
/* A wash of the class colour behind the header, fading out before the text it sits under. */
.class-card::before {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 96px; pointer-events: none;
  background: linear-gradient(180deg, color-mix(in srgb, var(--cc) 16%, transparent), transparent);
}
.cc-head { position: relative; display: flex; align-items: center; gap: 14px; margin-bottom: 12px; }
.cc-emblem {
  flex: none; width: 56px; height: 56px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 34px; color: var(--cc);
  background: var(--carve); border: 2px solid var(--cc);
  box-shadow: 0 0 16px color-mix(in srgb, var(--cc) 35%, transparent);
}
.cc-emblem svg { display: block; }
.cc-head h3 { margin: 0; font-size: 23px; letter-spacing: 2px; color: var(--cc); }
.cc-head h3 a { color: inherit; text-decoration: none; }
.cc-head h3 a:hover { text-decoration: underline; }
.cc-more { position: relative; align-self: flex-end; margin-top: 12px; font-size: 13px; }
.cc-wields { color: var(--dim); font-size: 12px; }
.cc-desc { position: relative; font-size: 13.5px; margin-bottom: 14px; }
.cc-specs { display: grid; gap: 10px; margin-top: auto; }
.cc-spec {
  background: rgba(0, 0, 0, 0.20); border: 1px solid var(--border);
  border-left: 3px solid var(--cc); padding: 10px 14px;
}
.cc-spec-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.cc-spec-name {
  font-family: var(--f-display); font-weight: 700; font-size: 16px;
  letter-spacing: 1px; color: var(--gold-lit);
}
.cc-spec-line { color: var(--dim); font-size: 12px; margin: 1px 0 6px; }
.cc-spec-blurb { font-size: 13px; line-height: 1.55; margin: 0; }

/* Role pills, in the inks a class page's spec tabs wear too (`ROLE_INKS`). */
.role {
  flex: none; font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase;
  padding: 1px 8px; border: 1px solid;
}
.role-tank { color: #93b8dc; border-color: #3d5670; background: rgba(61, 86, 112, 0.28); }
.role-healer { color: #7fd07f; border-color: #3d6b3d; background: rgba(61, 107, 61, 0.28); }
.role-damage { color: #e3a877; border-color: #6e4a2e; background: rgba(110, 74, 46, 0.28); }

/* Five rarities, each chip in the colour the game writes that rarity's item names in. */
.rarities {
  display: grid; gap: 8px; margin: 14px 0 18px;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
}
.rarity {
  text-align: center; padding: 10px 12px;
  background: rgba(0, 0, 0, 0.20); border: 1px solid var(--border); border-top: 3px solid var(--rc);
}
.rarity-name {
  display: block; color: var(--rc);
  font-family: var(--f-display); font-weight: 700; letter-spacing: 1px;
}
.rarity-count { color: var(--dim); font-size: 12px; }

.stats {
  display: grid; gap: 0 28px; margin: 12px 0 8px;
  grid-template-columns: repeat(auto-fit, minmax(min(380px, 100%), 1fr));
}
.stat {
  display: grid; grid-template-columns: 150px 1fr; gap: 14px;
  padding: 11px 0; border-bottom: 1px solid var(--border);
}
.stat dt { color: var(--gold); font-family: var(--f-display); font-weight: 700; font-size: 14px; }
.stat dd { color: var(--dim); font-size: 13px; margin: 0; }

/* ── Footer ── */
.site-foot {
  border-top: 2px solid var(--border2); background: rgba(0, 0, 0, 0.26);
  box-shadow: inset 0 1px 0 rgba(255, 235, 190, 0.06);
  padding: 20px 24px;
  text-align: center; color: var(--dim); font-size: 12px;
}
.site-foot a { color: var(--dim); }
.site-foot a:hover { color: var(--gold); }
.site-foot .dot { margin: 0 10px; opacity: 0.5; }

@media (max-width: 620px) {
  main { padding: 30px 16px 56px; }
  /* The base rule loses to `main.under-band` on specificity, so the window restates its own. */
  main.under-band { width: calc(100% - 20px); margin: 18px auto 30px; padding: 22px 18px 30px; }
  main.under-band::before { inset: 5px; }
  .hero h1 { font-size: 32px; letter-spacing: 4px; }
  .band-in { width: calc(100% - 20px); margin: 18px auto; padding: 18px 18px 16px; }
  .band-in.wide { width: auto; margin: 18px 10px; }
  .band-in.crested { gap: 16px; align-items: flex-start; }
  .crest-mark { width: 60px; height: 60px; font-size: 34px; }
  .dl { padding: 15px 30px; font-size: 15px; }
  .class-card { padding: 16px 14px 16px; }
  .stat { grid-template-columns: 1fr; gap: 2px; }
  /* The key column is nowrap on desktop to keep a row on one line; at this width that is
     what pushes the download page's requirements table wider than the screen. */
  td.k { width: auto; white-space: normal; }
}
