/*
 * Ages of Norrath — site styles.
 *
 * Hand-written rather than generated: production has no Node runtime, and a
 * single static stylesheet means there is no build step to get wrong during an
 * FTP deploy.
 *
 * The palette is original work in an EQ-adjacent fantasy register — aged
 * parchment, tarnished bronze, deep dusk blue. No Daybreak art, logos,
 * wordmark stylings, UI textures or extracted game imagery are used anywhere.
 */

:root {
  --bg: #14161c;
  --bg-raised: #1c1f28;
  --bg-sunken: #101218;
  --border: #2c3140;
  --border-bright: #3c4356;

  --ink: #e6e2d8;
  --ink-dim: #a8a396;
  --ink-faint: #767264;

  --bronze: #c69749;
  --bronze-dim: #8d6c34;
  --verdigris: #4d9e8a;
  --ember: #c25c3a;

  --up: #5fb371;
  --down: #b3505a;
  --unknown: #7b7f8c;
  --locked: #c69749;

  /* One content width for the whole site. Every page uses it; nothing else
     defines its own, which is what stops pages drifting to different widths. */
  --content-width: 1180px;

  --radius: 4px;
  --shadow: 0 2px 12px rgba(0, 0, 0, 0.45);

  --font-display: "Palatino Linotype", "Book Antiqua", Palatino, Georgia, serif;
  --font-body: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;
  --font-mono: "JetBrains Mono", "Cascadia Mono", Consolas, monospace;
}

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { font-family: var(--font-display); font-weight: 600; line-height: 1.25; margin: 0 0 .5em; }
h1 { font-size: 2rem; letter-spacing: .01em; }
h2 { font-size: 1.4rem; color: var(--bronze); }
h3 { font-size: 1.1rem; }

a { color: var(--bronze); text-decoration: none; }
a:hover { color: var(--ink); text-decoration: underline; }

code, pre, .mono { font-family: var(--font-mono); font-size: .875em; }
pre {
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem;
  overflow-x: auto;
}

.wrap { max-width: var(--content-width); margin: 0 auto; padding: 0 1.5rem; }

/*
 * The single exception, and only for sign-in style forms. A login box stretched
 * to 1180px is worse than a narrow one — the eye has to travel the full width
 * between a label and its field. Content pages must never use this; that is how
 * the site ended up with pages at three different widths.
 */
.wrap-form { max-width: 460px; margin: 0 auto; padding: 0 1.5rem; }

/* ---------------------------------------------------------------- header */

.site-header {
  border-bottom: 1px solid var(--border);
  background: linear-gradient(180deg, #191c24, #14161c);
  position: sticky; top: 0; z-index: 40;
}
.site-header .wrap { display: flex; align-items: center; gap: 1.5rem; height: 62px; }

.brand { display: flex; align-items: center; gap: .6rem; font-family: var(--font-display); font-size: 1.15rem; color: var(--ink); }
.brand:hover { text-decoration: none; color: var(--bronze); }
.brand .sigil { width: 26px; height: 26px; flex: none; }

.nav { display: flex; gap: 1.1rem; margin-left: auto; align-items: center; flex-wrap: wrap; }
.nav a { color: var(--ink-dim); font-size: .92rem; position: relative; padding: .35rem 0; }
.nav a:hover { color: var(--bronze); text-decoration: none; }

/* The current page stays lit and carries an underline, so hovering a different
   link cannot make it look as though you have navigated away. */
.nav a.active { color: var(--bronze); text-decoration: none; }
.nav a.active::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -2px;
  height: 2px; background: var(--bronze); border-radius: 2px;
}

/* ---------------------------------------------------------------- layout */

main { padding: 2rem 0 3rem; min-height: 60vh; }

.grid { display: grid; gap: 1rem; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }

/*
 * Cards are for things that genuinely are separate objects — a server, a form,
 * a staff member. Body copy does not need a box around it; wrapping every
 * paragraph in one is what made the site read as a pile of bubbles.
 */
.card {
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.1rem 1.2rem;
}
.card h2, .card h3 { margin-top: 0; }

.muted { color: var(--ink-dim); }
.faint { color: var(--ink-faint); font-size: .875rem; }
.small { font-size: .875rem; }
.center { text-align: center; }
.right { text-align: right; }
.stack > * + * { margin-top: 1rem; }
.row { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }

/* ---------------------------------------------------------------- status */

.status-banner {
  border-radius: var(--radius);
  padding: .8rem 1rem;
  margin-bottom: 1.25rem;
  border: 1px solid;
  font-size: .95rem;
}
.status-banner.ok { border-color: rgba(95,179,113,.35); background: rgba(95,179,113,.08); color: #a9d9b4; }
.status-banner.warn { border-color: rgba(198,151,73,.4); background: rgba(198,151,73,.09); color: #e3c48b; }

/* When the login server is unreachable the whole list is greyed: we do not
   know these worlds are down, only that we have no current information. */
.worlds.unknown { opacity: .55; filter: grayscale(.5); }

.world {
  display: grid;
  grid-template-columns: 12px minmax(0, 1fr) auto;
  gap: .8rem;
  align-items: center;
  padding: .7rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-raised);
}
.world + .world { margin-top: .35rem; }

.dot { width: 11px; height: 11px; border-radius: 50%; flex: none; }
.dot.up { background: var(--up); box-shadow: 0 0 8px rgba(95,179,113,.6); }
.dot.down { background: var(--down); }
.dot.locked { background: var(--locked); }
.dot.unknown { background: var(--unknown); }

/* Name and tags share a line and wrap together rather than the tags claiming
   a row of their own. */
.world-main { display: flex; align-items: baseline; gap: .45rem; flex-wrap: wrap; min-width: 0; }
.world-name { font-family: var(--font-display); font-size: 1.05rem; }
.world-meta { flex-basis: 100%; color: var(--ink-dim); font-size: .85rem; }

.world-pop { text-align: right; font-family: var(--font-mono); line-height: 1.15; }
.world-pop .n { font-size: 1.3rem; color: var(--ink); }
.world-pop .label { display: block; font-size: .68rem; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-faint); }

/*
 * Compact: the home rail and the widget.
 *
 * One line per server. Five of them should be readable without scrolling,
 * which is the whole reason the rail exists — it is a glance, not a page.
 */
.worlds.compact .world { padding: .45rem .7rem; gap: .6rem; grid-template-columns: 9px minmax(0, 1fr) auto; }
.worlds.compact .world + .world { margin-top: .25rem; }
.worlds.compact .dot { width: 9px; height: 9px; }
.worlds.compact .world-name { font-size: .95rem; }
.worlds.compact .badge { font-size: .62rem; padding: .05rem .38rem; }
.worlds.compact .world-pop .n { font-size: 1rem; }
.worlds.compact .world-pop .label { display: none; }

.badge {
  display: inline-block;
  padding: .12rem .5rem;
  border-radius: 999px;
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  border: 1px solid var(--border-bright);
  color: var(--ink-dim);
}
.badge.era { border-color: var(--bronze-dim); color: var(--bronze); }
/* `warn`, not `alert`. `.alert` further down is a whole flash-message box —
   1rem padding, its own font size, a bottom margin — and it is declared after
   this block, so `class="badge alert"` lost every property they share and
   rendered as a fat callout. Badge modifiers must not borrow the name of a
   component; that collision is silent and only shows up on screen. */
.badge.warn { border-color: var(--ember); color: var(--ember); }
.badge.good { border-color: var(--up); color: var(--up); }

/* A badge inside a heading must not inherit the heading.
   Headings are 600-weight Palatino, and a badge picking that up renders heavy
   and serif — which is what made the game-account tags look bolted on. Badges
   are labels wherever they appear, so they keep their own type. */
h1 .badge, h2 .badge, h3 .badge, h4 .badge, summary .badge {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: .66rem;
  letter-spacing: .08em;
  vertical-align: middle;
  position: relative;
  top: -.08em;
  margin-left: .15rem;
}

/* The era tag carries a padlock, so it lays out its children rather than
   treating them as a run of text. */
.badge.era { display: inline-flex; align-items: center; gap: .3rem; }
.badge .lock { width: .62em; height: .72em; flex: none; }

/* Sits with the tags: what this server is right now, on one line. */
.norrath-date { white-space: nowrap; }
.norrath-date strong { color: var(--ink-dim); font-weight: 400; }
.progression-mark { cursor: help; }

/* Ruleset. Colour alone never carries this — the tag says PvE or PvP in
   words, and the colour is there to make the difference scannable. Red for
   PvP because that is the one you can be killed on. */
.badge.ruleset.pve { border-color: var(--up); color: var(--up); }
.badge.ruleset.pvp { border-color: var(--down); color: var(--down); }

/* Forum index headings. Same treatment as the server-status groups — both are
   a heading over a list of rows, and they should not read as two ideas. */
.forum-section {
  margin: 1.8rem 0 .6rem;
  font-family: var(--font-display);
  font-size: .8rem;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--ink-faint);
  border-bottom: 1px solid var(--border);
  padding-bottom: .4rem;
}
.forum-section:first-child { margin-top: 0; }

.section-admin { margin-top: .9rem; flex-wrap: wrap; }

.world-group {
  margin: 1.4rem 0 .5rem;
  font-family: var(--font-display);
  font-size: .78rem;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--ink-faint);
}
.world-group:first-child { margin-top: 0; }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
.sr-only[hidden] { display: none; }

/* ---------------------------------------------------------------- hero */

/*
 * Full-bleed hero. The previous home page put the pitch, the cycle table and
 * the status list in two narrow columns at once, which left everything
 * cramped. The hero now owns the top of the page on its own, and the detail
 * that used to compete with it lives on /about.
 */
.hero {
  border-bottom: 1px solid var(--border);
  background:
    radial-gradient(900px 380px at 20% -10%, rgba(198,151,73,.13), transparent 60%),
    radial-gradient(700px 320px at 85% 0%, rgba(77,158,138,.10), transparent 60%),
    linear-gradient(180deg, #191c24, var(--bg));
  padding: 3.25rem 0 2.5rem;
}
.hero h1 {
  font-size: clamp(2rem, 4vw, 2.9rem);
  line-height: 1.12;
  margin-bottom: .7rem;
  letter-spacing: -0.01em;
}
.hero .lede {
  font-size: clamp(1rem, 1.4vw, 1.1rem);
  color: var(--ink-dim);
  max-width: 78ch;
  margin: 0 0 1.5rem;
}
.hero .actions { display: flex; gap: .75rem; flex-wrap: wrap; }
.hero .btn { padding: .7rem 1.4rem; font-size: 1rem; }

/* Interior pages get a shorter hero: enough to frame the page, not enough to
   push the content people came for below the fold. */
.hero.compact { padding: 2.25rem 0 1.75rem; }
.hero.compact h1 { font-size: clamp(1.8rem, 3.2vw, 2.3rem); }
.hero.compact .lede { margin-bottom: 0; }

/* Sits in the hero beside the title — a live figure, era label or similar. */
.hero-aside {
  display: flex; align-items: baseline; gap: 1.5rem;
  flex-wrap: wrap; margin-top: 1.1rem;
}
.hero-aside .figure { font-family: var(--font-display); font-size: 1.6rem; color: var(--bronze); }
.hero-aside .figure small {
  display: block; font-family: var(--font-body); font-size: .7rem;
  text-transform: uppercase; letter-spacing: .08em; color: var(--ink-faint);
}

/* Prose pages (setup guide, legal) — the hero carries the title, so the body
   should not repeat it. */
.page-body { padding-top: 1.75rem; }

/* Home layout: news is the page, status is a companion rail. */
.home-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 330px;
  gap: 2.5rem;
  align-items: start;
  padding-top: 2rem;
}
.rail { position: sticky; top: 78px; display: grid; gap: 1rem; }

.section-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; margin-bottom: 1.1rem;
  padding-bottom: .45rem; border-bottom: 1px solid var(--border);
}
.section-head h2 { margin: 0; }

/* Body copy that is not inside .prose but still wants a readable measure. */
.measure { max-width: 88ch; }

/* Roomier news entries — the old cards packed four to a grid and read as tiles
   rather than as something anyone would actually sit and read. */
.post { padding-bottom: 1.6rem; margin-bottom: 1.6rem; border-bottom: 1px solid var(--border); }
.post:last-child { border-bottom: 0; }
.post h3 { font-size: 1.35rem; margin-bottom: .3rem; }
.post h3 a { color: var(--ink); }
.post h3 a:hover { color: var(--bronze); text-decoration: none; }
.post .meta { color: var(--ink-faint); font-size: .85rem; margin-bottom: .9rem; display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }
.post .summary { color: var(--ink-dim); margin: 0 0 1rem; line-height: 1.7; }
.post .more { font-size: .9rem; }

/* ---------------------------------------------------------------- staff */

.staff-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 1rem; }

.staff-card {
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.15rem;
  text-align: center;
}
.staff-portrait, .staff-initials {
  width: 96px; height: 96px; border-radius: 50%;
  margin: 0 auto .7rem; display: block;
  border: 2px solid var(--bronze-dim);
  object-fit: cover;
}
.staff-initials {
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-sunken);
  font-family: var(--font-display); font-size: 2.1rem; color: var(--bronze);
}
.staff-card .name { font-family: var(--font-display); font-size: 1.2rem; margin-bottom: .1rem; }
.staff-card .character { color: var(--verdigris); font-size: .9rem; margin-bottom: .1rem; }
.staff-card .role { font-size: .72rem; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-faint); margin-bottom: .8rem; }
.staff-card .blurb { color: var(--ink-dim); font-size: .9rem; line-height: 1.65; text-align: left; }

/* Cycle explainer, moved off the home page. */
.cycle-steps { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; margin: 1rem 0 1.5rem; }
.cycle-step {
  border: 1px solid var(--bronze-dim); color: var(--bronze);
  border-radius: 999px; padding: .35rem .9rem; font-family: var(--font-display);
}
.cycle-arrow { color: var(--ink-faint); }

.server-card {
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 1.05rem 1.15rem; background: var(--bg-raised);
}
.server-card h3 { margin-bottom: .2rem; }
.server-card .era-line { color: var(--verdigris); font-size: .85rem; margin-bottom: .6rem; }
.server-card p { color: var(--ink-dim); font-size: .92rem; margin: 0; line-height: 1.65; }

@media (max-width: 900px) {
  .home-grid { grid-template-columns: 1fr; gap: 2.5rem; padding-top: 2rem; }
  .rail { position: static; }
  .hero { padding: 3rem 0 2.5rem; }
}

/* ---------------------------------------------------------------- forms */

label { display: block; font-size: .875rem; color: var(--ink-dim); margin-bottom: .3rem; }

/* Every text-like control, styled by exclusion rather than by listing types.
   An enumerated list silently misses anything not on it — `type=url`, and
   inputs written with no type attribute at all, which are valid and behave as
   text. Those fell through to browser defaults and rendered as white boxes
   next to correctly styled selects. Excluding the handful of controls that
   must keep their native appearance cannot go stale the same way. */
input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=reset]):not([type=image]):not([type=range]):not([type=color]),
textarea, select {
  width: 100%;
  padding: .55rem .7rem;
  background: var(--bg-sunken);
  border: 1px solid var(--border-bright);
  border-radius: var(--radius);
  color: var(--ink);
  font: inherit;
  line-height: 1.35;
}
input:focus, textarea:focus, select:focus { outline: 2px solid var(--bronze-dim); outline-offset: 1px; }

textarea { min-height: 220px; font-family: var(--font-mono); font-size: .875rem; line-height: 1.55; }

/* An author who wrote rows= meant that many rows. The tall default is for the
   long-form boxes that do not specify one. */
textarea[rows] { min-height: 0; }

/* File inputs keep the native button — restyling it fully is a rabbit hole —
   but the surrounding box should not look foreign. */
input[type=file] {
  width: 100%;
  padding: .45rem .6rem;
  background: var(--bg-sunken);
  border: 1px solid var(--border-bright);
  border-radius: var(--radius);
  color: var(--ink-dim);
  font: inherit;
  font-size: .875rem;
}

.field + .field { margin-top: 1rem; }
.checkline { display: flex; gap: .5rem; align-items: flex-start; }
.checkline input { margin-top: .35rem; }
.checkline label { margin: 0; color: var(--ink); }

.btn {
  display: inline-block;
  padding: .55rem 1.05rem;
  border-radius: var(--radius);
  border: 1px solid var(--bronze-dim);
  background: linear-gradient(180deg, #3a3324, #2a251a);
  color: var(--bronze);
  font: inherit;
  font-size: .92rem;
  cursor: pointer;
}
.btn:hover { color: var(--ink); border-color: var(--bronze); text-decoration: none; }
.btn.primary { background: linear-gradient(180deg, #6d5426, #513e1c); color: #f2e6cd; border-color: var(--bronze); }
.btn.ghost { background: transparent; border-color: var(--border-bright); color: var(--ink-dim); }
.btn.danger { border-color: #7a3339; color: #e08b92; background: linear-gradient(180deg, #33201f, #241717); }
.btn.small { padding: .3rem .65rem; font-size: .8rem; }

/* ---------------------------------------------------------------- alerts */

.alert { border-radius: var(--radius); padding: .8rem 1rem; margin-bottom: 1.25rem; border: 1px solid; font-size: .93rem; }
.alert.ok { border-color: rgba(95,179,113,.4); background: rgba(95,179,113,.09); color: #b0dcba; }
.alert.err { border-color: rgba(179,80,90,.45); background: rgba(179,80,90,.1); color: #e79aa1; }
.alert.warn { border-color: rgba(198,151,73,.45); background: rgba(198,151,73,.1); color: #e3c48b; }
.alert ul { margin: .4rem 0 0; padding-left: 1.1rem; }

/* ---------------------------------------------------------------- tables */

table { width: 100%; border-collapse: collapse; font-size: .9rem; }
th, td { text-align: left; padding: .6rem .7rem; border-bottom: 1px solid var(--border); vertical-align: top; }
th { color: var(--ink-faint); font-weight: 600; text-transform: uppercase; font-size: .72rem; letter-spacing: .07em; }
tbody tr:hover { background: rgba(255,255,255,.02); }
.table-scroll { overflow-x: auto; }

/* ---------------------------------------------------------------- prose */

/*
 * Prose measure.
 *
 * 90ch rather than the typographic ideal of ~68ch: the container is 1180px, and
 * a 68ch column inside it left two fifths of the page empty and read as broken
 * rather than as considered. This is the only place a measure is applied, so
 * text is never constrained twice.
 */
.prose { max-width: 90ch; }
.prose h2 { margin-top: 1.9rem; }
.prose ul, .prose ol { padding-left: 1.3rem; }
.prose blockquote {
  margin: 1.2rem 0; padding: .2rem 0 .2rem 1rem;
  border-left: 3px solid var(--bronze-dim); color: var(--ink-dim);
}
.prose img { max-width: 100%; height: auto; border-radius: var(--radius); border: 1px solid var(--border); }
.prose table { margin: 1.2rem 0; }

/* ---------------------------------------------------------------- admin */

/*
 * The rail carries a dozen links across five groups, so it needs room to be
 * scannable. A tighter column saves nothing — the content area is nowhere near
 * short of width at 1180px.
 */
.admin-shell { display: grid; grid-template-columns: 244px minmax(0, 1fr); gap: 2.5rem; }

.admin-nav {
  position: sticky; top: 80px; align-self: start;
  border-right: 1px solid var(--border);
  padding-right: 1rem;
}
.admin-nav a {
  display: flex; justify-content: space-between; align-items: center; gap: .5rem;
  padding: .55rem .75rem; margin-bottom: .12rem;
  border-radius: var(--radius);
  color: var(--ink-dim); font-size: .93rem; line-height: 1.35;
}
.admin-nav a:hover { background: var(--bg-raised); color: var(--ink); text-decoration: none; }
.admin-nav a.active {
  background: var(--bg-raised); color: var(--bronze);
  box-shadow: inset 2px 0 0 var(--bronze);
}
/* Enough gap to separate the groups, not enough to push the last one below
   the fold. The links keep their own padding — the earlier loosening was about
   tap targets, which this does not touch. */
.admin-nav .section {
  font-size: .68rem; text-transform: uppercase; letter-spacing: .1em;
  color: var(--ink-faint); margin: .8rem .75rem .25rem;
}
.admin-nav .section:first-child { margin-top: 0; }

.count-badge {
  background: var(--ember); color: #fff; border-radius: 999px;
  font-size: .7rem; padding: .05rem .45rem; font-weight: 600;
}

.stat { text-align: center; padding: 1rem; }
.stat .n { font-family: var(--font-display); font-size: 2rem; color: var(--bronze); display: block; }
.stat .label { font-size: .78rem; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-faint); }

.admin-whoami { padding: .2rem .75rem; }

/* --------------------------------------------- admin category summaries */

.cat-admin {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-raised);
  margin-bottom: .5rem;
}
.cat-admin > summary {
  list-style: none;
  cursor: pointer;
  padding: .8rem 1rem;
}
.cat-admin > summary::-webkit-details-marker { display: none; }
.cat-admin[open] > summary { border-bottom: 1px solid var(--border); }

.cat-admin-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  gap: 1.5rem;
  align-items: center;
}
.cat-admin-name { font-family: var(--font-display); font-size: 1.05rem; color: var(--ink); }

/* Facts read as text rather than as form fields, which is what makes the
   list scannable. */
.cat-admin-facts { display: flex; gap: 1.5rem; margin: 0; }
.cat-admin-facts div { text-align: left; }
.cat-admin-facts dt {
  font-size: .64rem; text-transform: uppercase; letter-spacing: .08em;
  color: var(--ink-faint); margin: 0;
}
.cat-admin-facts dd { margin: 0; font-size: .88rem; color: var(--ink-dim); white-space: nowrap; }

.cat-admin[open] .cat-admin-toggle { border-color: var(--bronze); color: var(--bronze); }
.cat-admin-body { padding: 1rem; }

/* Archived boards are dimmed but still legible — they remain staff-usable. */
.cat-admin.archived { opacity: .72; border-style: dashed; }

@media (max-width: 860px) {
  .cat-admin-head { grid-template-columns: 1fr; gap: .6rem; }
  .cat-admin-facts { flex-wrap: wrap; gap: 1rem; }
}

.secret-reveal {
  background: var(--bg-sunken); border: 1px dashed var(--bronze-dim);
  border-radius: var(--radius); padding: 1rem; word-break: break-all;
}

/* ---------------------------------------------------------------- forum */

.cat-row, .thread-row {
  display: grid;
  align-items: center;
  gap: 1rem;
  padding: .8rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-raised);
}
.cat-row + .cat-row, .thread-row + .thread-row { margin-top: .4rem; }

.cat-row { grid-template-columns: minmax(0,1fr) 90px 220px; }
.thread-row { grid-template-columns: minmax(0,1fr) 80px 80px 140px; }

.cat-row h3, .thread-row h3 { margin: 0 0 .15rem; font-size: 1.05rem; }
.cat-row h3 a, .thread-row h3 a { color: var(--ink); }
.cat-row h3 a:hover, .thread-row h3 a:hover { color: var(--bronze); text-decoration: none; }

.cat-count, .thread-stat { text-align: center; }
.cat-count .n, .thread-stat .n { display: block; font-family: var(--font-display); font-size: 1.2rem; color: var(--bronze); }
.cat-count .label, .thread-stat .label { font-size: .68rem; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-faint); }

/* An unread thread gets a bronze edge rather than bold text, which stays
   legible when most of the page is unread. */
.thread-row.unread { border-left: 3px solid var(--bronze); }

/* ---------------------------------------------------------------- posts */

.post-card {
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-raised);
  margin-bottom: .8rem;
  overflow: hidden;
}

.post-author {
  padding: 1rem .8rem;
  text-align: center;
  background: var(--bg-sunken);
  border-right: 1px solid var(--border);
}
.post-avatar {
  width: 64px; height: 64px; border-radius: 50%;
  margin: 0 auto .5rem; display: block;
  border: 2px solid var(--bronze-dim); object-fit: cover;
}
.post-avatar.initials {
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-raised); font-family: var(--font-display);
  font-size: 1.3rem; color: var(--bronze);
}
.post-author-name { font-family: var(--font-display); font-size: 1rem; }
.post-author-title { font-size: .7rem; text-transform: uppercase; letter-spacing: .07em; color: var(--verdigris); margin-bottom: .3rem; }

.post-body { padding: 1rem 1.15rem; min-width: 0; }
.post-meta { margin-bottom: .6rem; }

.quote-ref {
  border-left: 2px solid var(--border-bright);
  padding-left: .6rem; margin-bottom: .6rem;
}

.post-content { line-height: 1.7; }
.post-content img { max-width: 100%; height: auto; border-radius: var(--radius); }
.post-content blockquote {
  border-left: 3px solid var(--bronze-dim);
  margin: .8rem 0; padding: .2rem 0 .2rem .9rem; color: var(--ink-dim);
}
.post-content pre { background: var(--bg-sunken); padding: .8rem; border-radius: var(--radius); overflow-x: auto; }

.post-signature {
  margin-top: 1rem; padding-top: .6rem;
  border-top: 1px solid var(--border);
  color: var(--ink-faint); font-size: .85rem;
}

.post-actions { display: flex; gap: .4rem; margin-top: .9rem; flex-wrap: wrap; align-items: center; }
.post-actions form { display: inline; }
.btn.liked { border-color: var(--verdigris); color: var(--verdigris); }

/* ---------------------------------------------------------------- polls */

.poll-choice {
  display: flex; align-items: center; gap: .5rem;
  padding: .45rem .6rem; margin-bottom: .3rem;
  border: 1px solid var(--border); border-radius: var(--radius);
  cursor: pointer; color: var(--ink);
}
.poll-choice:hover { border-color: var(--bronze-dim); }
.poll-choice input { width: auto; margin: 0; }

.poll-result { margin-bottom: .7rem; }
.poll-result-head { display: flex; justify-content: space-between; gap: 1rem; margin-bottom: .25rem; font-size: .92rem; }
.poll-bar { height: 8px; background: var(--bg-sunken); border-radius: 999px; overflow: hidden; }
.poll-bar span { display: block; height: 100%; background: linear-gradient(90deg, var(--bronze-dim), var(--bronze)); }

.poll-builder summary { cursor: pointer; color: var(--bronze); font-size: .92rem; }

/* ------------------------------------------------------------ moderation */

.mod-bar {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  margin-top: 1rem; padding: .6rem .8rem;
  border: 1px dashed var(--bronze-dim); border-radius: var(--radius);
  background: rgba(198,151,73,.05);
}
.mod-bar form { display: inline; }
.mod-bar-label {
  font-size: .68rem; text-transform: uppercase; letter-spacing: .08em;
  color: var(--bronze); margin-right: .3rem;
}

.reported-post {
  border: 1px solid var(--border); border-left: 3px solid var(--ember);
  border-radius: var(--radius); padding: .7rem .9rem; background: var(--bg-sunken);
}

/* ------------------------------------------------------------ the editor */

.aon-editor {
  background: var(--bg-sunken);
  border: 1px solid var(--border-bright);
  border-radius: var(--radius);
  color: var(--ink);
  padding: .7rem;
}
.aon-editor:empty::before { color: var(--ink-faint); }

/* Trix ships a light-theme toolbar; recolour it rather than fight it. */
trix-toolbar .trix-button-group { border-color: var(--border-bright); background: var(--bg-raised); }
trix-toolbar .trix-button {
  background: transparent; border-color: var(--border-bright);
  color: var(--ink-dim);
}
trix-toolbar .trix-button:not(:disabled):hover { background: var(--bg-sunken); }
trix-toolbar .trix-button.trix-active { background: var(--bronze-dim); color: #fff; }
trix-toolbar .trix-button--icon::before { filter: invert(85%); }
trix-toolbar .trix-dialog { background: var(--bg-raised); border-color: var(--border-bright); color: var(--ink); }
trix-toolbar .trix-input { background: var(--bg-sunken); color: var(--ink); border-color: var(--border-bright); }

@media (max-width: 780px) {
  .cat-row { grid-template-columns: 1fr; }
  .thread-row { grid-template-columns: 1fr 70px; }
  .cat-latest, .thread-latest { display: none; }
  .post-card { grid-template-columns: 1fr; }
  .post-author {
    display: flex; align-items: center; gap: .7rem; text-align: left;
    border-right: 0; border-bottom: 1px solid var(--border); padding: .6rem .8rem;
  }
  .post-avatar { width: 40px; height: 40px; margin: 0; }
}

/* ---------------------------------------------------------------- media */

.media-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 1rem; }

.media-item {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-raised);
  overflow: hidden;
}
.media-item img {
  display: block; width: 100%; height: 140px;
  object-fit: cover; background: var(--bg-sunken);
}
.media-meta { padding: .6rem; display: grid; gap: .4rem; justify-items: start; }
.media-meta form { width: 100%; }
.media-meta input { width: 100%; }

/* ---------------------------------------------------------------- footer */

.site-footer {
  border-top: 1px solid var(--border);
  padding: 1.5rem 0 1.25rem;
  color: var(--ink-faint);
  font-size: .82rem;
  background: var(--bg-sunken);
}
.site-footer a { color: var(--ink-dim); }

/* Disclaimer and links side by side rather than stacked. */
.footer-top {
  display: flex;
  gap: 3rem;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
}
.footer-about { flex: 1 1 340px; min-width: 0; }
.site-footer .disclaimer { margin: 0; max-width: 62ch; line-height: 1.5; }

.footer-cols { display: flex; gap: 2.5rem; flex-wrap: wrap; }
.footer-cols ul { list-style: none; margin: .3rem 0 0; padding: 0; }
.footer-cols li { margin-bottom: .2rem; }

.footer-bottom {
  margin: 1.25rem 0 0;
  padding-top: .9rem;
  border-top: 1px solid var(--border);
}

@media (max-width: 720px) {
  .footer-top { gap: 1.5rem; }
  .footer-cols { gap: 1.75rem; }
}

@media (max-width: 860px) {
  .admin-shell { grid-template-columns: 1fr; }
  .admin-nav { position: static; }
  .site-header .wrap { height: auto; padding-top: .8rem; padding-bottom: .8rem; flex-wrap: wrap; }
  .nav { margin-left: 0; width: 100%; }
}

/* --- Wiki -------------------------------------------------------------- */
/* Data-dense pages: tighter than the editorial pages, because an item has
   forty attributes and a loot table has hundreds of rows. */

.wiki-section { margin: 1.6rem 0; }
.wiki-section h2 {
    font-size: 1.05rem; text-transform: uppercase; letter-spacing: .06em;
    color: var(--muted); border-bottom: 1px solid var(--line);
    padding-bottom: .35rem; margin-bottom: .8rem;
}

.wiki-fields { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: .5rem 1.4rem; margin: 0; }
.wiki-fields > div { display: flex; justify-content: space-between; gap: 1rem; padding: .3rem 0; border-bottom: 1px dotted var(--line); }
.wiki-fields dt { color: var(--muted); font-size: .88rem; }
.wiki-fields dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }

/* Wide tables scroll inside their own box rather than pushing the page sideways. */
.table-scroll { overflow-x: auto; }
.wiki-table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.wiki-table th, .wiki-table td { text-align: left; padding: .35rem .6rem; border-bottom: 1px solid var(--line); white-space: nowrap; }
.wiki-table th { color: var(--muted); font-weight: 600; font-size: .82rem; text-transform: uppercase; letter-spacing: .04em; }
.wiki-table tbody tr:hover { background: var(--panel-2, rgba(255,255,255,.03)); }

.wiki-backlink-head { font-size: .95rem; margin: 1rem 0 .4rem; }
.wiki-backlink-head .muted { font-weight: 400; margin-left: .5rem; }
.wiki-backlinks { list-style: none; padding: 0; margin: 0; columns: 2; column-gap: 2rem; }
.wiki-backlinks li { break-inside: avoid; padding: .2rem 0; font-size: .92rem; }
.wiki-backlinks .muted { margin-left: .4rem; }

@media (max-width: 640px) {
    .wiki-backlinks { columns: 1; }
    .wiki-fields > div { flex-direction: column; gap: 0; }
    .wiki-fields dd { text-align: left; }
}

/* Wiki images placed by a template. */
.wiki-images { display: grid; gap: .8rem; }
.wiki-images.gallery { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
.wiki-images.single { grid-template-columns: 1fr; }
.wiki-images figure { margin: 0; }
.wiki-images img { width: 100%; height: auto; border-radius: 6px; display: block; }
.wiki-images figcaption { margin-top: .3rem; }



/* Layout-editor section blocks sit inside a card, so they read as nested
   rather than competing with it. */
.section-box { border: 1px solid var(--line); border-radius: 8px; padding: .9rem 1rem; margin-bottom: .8rem; background: rgba(255,255,255,.02); }
.section-head { display: flex; gap: .5rem; align-items: center; margin-bottom: .7rem; }
.section-head strong { white-space: nowrap; }
.section-head input { flex: 1; min-width: 8rem; }
.section-row { display: flex; gap: .5rem; align-items: center; margin-bottom: .45rem; }
.section-row select { min-width: 14rem; }
.section-row input { flex: 1; }
.section-box .field { margin-bottom: .6rem; }

@media (max-width: 720px) {
    .section-head { flex-wrap: wrap; }
    .section-row { flex-wrap: wrap; }
    .section-row select, .section-row input { min-width: 0; width: 100%; }
}

/* Dense list of wiki page types: 47 rows should read as a table, not 47 cards. */
.type-list { padding: .25rem .35rem; }
.type-row {
  display: grid;
  grid-template-columns: minmax(9rem, 1.4fr) minmax(7rem, 1fr) 5.5rem 6.5rem auto;
  gap: .75rem;
  align-items: center;
  padding: .4rem .6rem;
  border-bottom: 1px solid var(--line);
}
.type-row:last-child { border-bottom: 0; }
.type-row:hover { background: rgba(255,255,255,.03); }
.type-row.is-hidden .type-name a { color: var(--ink-dim); }
.type-name { display: flex; align-items: center; gap: .4rem; min-width: 0; }
.type-name a { font-weight: 600; }
.type-row code { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.type-num { text-align: right; font-variant-numeric: tabular-nums; }
.type-row form { margin: 0; justify-self: end; }

@media (max-width: 780px) {
  .type-row { grid-template-columns: 1fr auto; row-gap: .2rem; }
  .type-row code, .type-num { grid-column: 1; text-align: left; }
}

/* Pagination. Replaces Laravel's Tailwind default, which matched nothing here. */
.pager { display: flex; align-items: center; gap: .35rem; flex-wrap: wrap; margin: 1.4rem 0 .5rem; }
.pager-pages { display: flex; gap: .25rem; flex-wrap: wrap; }
.pager-link {
  display: inline-block;
  min-width: 2.1rem;
  padding: .35rem .6rem;
  text-align: center;
  border: 1px solid var(--border-bright);
  border-radius: var(--radius);
  background: var(--bg-raised);
  color: var(--ink-dim);
  font-size: .875rem;
}
a.pager-link:hover { color: var(--ink); border-color: var(--bronze-dim); text-decoration: none; }
.pager-link.is-current { background: var(--bronze-dim); border-color: var(--bronze); color: var(--ink); font-weight: 600; }
.pager-link.is-disabled { opacity: .4; }
.pager-gap { padding: .35rem .2rem; color: var(--ink-faint); }

/* Pushed to the far end so the controls stay grouped together. */
.pager-count { margin-left: auto; }

@media (max-width: 640px) {
  .pager-count { margin-left: 0; flex-basis: 100%; }
}

/* Backlink group picker in the layout editor. */
.backlink-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; margin: .5rem 0; }
.backlink-row {
  display: grid;
  grid-template-columns: auto 1fr auto auto auto;
  gap: .6rem;
  align-items: center;
  padding: .35rem .6rem;
  border-bottom: 1px solid var(--line);
}
.backlink-row:last-child { border-bottom: 0; }
.backlink-row:hover { background: rgba(255,255,255,.03); }
.backlink-row input[type=checkbox] { width: auto; margin: 0; }
.backlink-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.backlink-row .faint { font-variant-numeric: tabular-nums; text-align: right; }

/* ------------------------------------------------- markdown layout helpers */
/* Applied from page/news Markdown as {.left.half}. The class list is
   allowlisted in HtmlSanitiser, so only these names ever reach the page. */

.prose img.left { float: left; margin: .2rem 1.2rem .8rem 0; }

.prose img.right { float: right; margin: .2rem 0 .8rem 1.2rem; }

.prose img.center { display: block; margin: 1rem auto; float: none; }

/* Widths are of the text column, so they stay sensible whatever the image's
   intrinsic size. */
.prose img.half   { width: 50%; }
.prose img.third  { width: 33%; }
.prose img.quarter{ width: 25%; }
.prose img.full   { width: 100%; float: none; display: block; margin: 1rem 0; }


/* A float must not escape its section and drag itself alongside the next
   heading, which is what makes floated images look broken. */
.prose h1, .prose h2, .prose h3 { clear: both; }

/* Below this a 50% image leaves a column of text too narrow to read. */
@media (max-width: 620px) {
  .prose img.left, .prose img.right {
    float: none; display: block; width: 100%; margin: 1rem 0;
  }
}

/* Server heading above each block of the team listing. */
.staff-group {
  font-size: .95rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--bronze);
  margin: 1.6rem 0 .7rem;
  padding-bottom: .3rem;
  border-bottom: 1px solid var(--line);
}
.staff-group:first-of-type { margin-top: .4rem; }

/* Native <dialog> styled to match the cards. Browser handles modality,
   focus trapping and Escape; this only supplies the skin. */
.aon-dialog {
  width: min(560px, calc(100vw - 2rem));
  padding: 1.25rem 1.35rem;
  background: var(--bg-raised);
  border: 1px solid var(--border-bright);
  border-radius: var(--radius);
  color: var(--ink);
  box-shadow: var(--shadow);
}
.aon-dialog::backdrop { background: rgba(0, 0, 0, .6); }

/* Daily-views bars. No chart library — there is no build step here, and the
   shape is the whole message. */
.spark { display: flex; align-items: flex-end; gap: 2px; height: 64px; }
.spark-bar { flex: 1; min-width: 2px; background: var(--bronze-dim); border-radius: 2px 2px 0 0; }
.spark-bar:hover { background: var(--bronze); }

/* Era progression bar on the Servers page. */
.progression { margin-top: .6rem; }
.progression-head { display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: .35rem; }

.progression-bar {
  position: relative;
  height: 8px;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: 999px;
  overflow: visible;
}
.progression-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--bronze-dim), var(--bronze));
  border-radius: 999px;
}

/* Markers sit at the era's real position along the run, so the spacing itself
   carries information. Centred on that point by transform rather than a fixed
   margin, so the label below can use exactly the same maths. */
.progression-mark {
  position: absolute; top: 50%;
  width: 9px; height: 9px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: var(--bg-sunken);
  border: 2px solid var(--border-bright);
}
.progression-mark.reached { background: var(--bronze); border-color: var(--bronze); }

/* Labels are positioned, not spaced. Laying them out with flex put them at
   even intervals while the dots sat at their real dates, so the two never
   lined up. Height is reserved because the children are taken out of flow. */
.progression-legend {
  position: relative;
  height: 2.7em;
  margin-top: .5rem;
}
.progression-legend > span {
  position: absolute;
  transform: translateX(-50%);
  text-align: center;
  line-height: 1.25;
  white-space: nowrap;
}

/* The ends would otherwise hang off the bar. */
.progression-legend > span.at-start { transform: none; text-align: left; }
.progression-legend > span.at-end { transform: translateX(-100%); text-align: right; }

.progression-legend .reached { color: var(--ink-dim); }

/* Below this the labels collide, so they stack into a plain list. */
@media (max-width: 560px) {
  .progression-legend { height: auto; display: flex; flex-wrap: wrap; gap: .75rem; }
  .progression-legend > span,
  .progression-legend > span.at-start,
  .progression-legend > span.at-end {
    position: static; transform: none; text-align: left;
  }
}

/* The progression bar sits inside the world row, spanning the full width of
   the name column rather than squeezing in beside the tags. */
.world-progression { flex-basis: 100%; margin-top: .5rem; }
.world-progression .progression { margin-top: 0; }

/* `.badge` sets display:inline-block, which beats the browser's own
   [hidden] { display: none } rule — so an empty era badge would render as a
   stray empty pill until the poller filled it in. */
.badge[hidden] { display: none; }
