/* ==========================================================================
   MU REALM - the site stylesheet.

   Classic MU fan-site frame geometry: 1140px wrapper, 518px header, 236px
   side columns. The dark frame carries every ornament (menu crown, corner
   brackets, sidebar plates); the parchment column stays quiet so the eye goes
   to the content.

   Art is generated (Replicate) and cut procedurally (Pillow); the pieces live
   in /assets/img/chaos/ and are WebP only.

   ⚠ The vertical chain in the header is interlocked: logo padding, the status
   strip and the menu bar add up to where .bottom-container starts. Change one
   height and the menu climbs onto the content.
   ========================================================================== */

:root {
  --deep:        #070d15;
  --navy:        #0e1a27;
  --navy-hi:     #16283a;
  --sea:         #1d3a50;
  --horizon:     #6d4a30;
  --ember:       #e2913f;
  --ember-hot:   #ffc070;
  --chrome:      #d6dde8;
  --chrome-dim:  #8d9bb0;
  --steel:       #5f7fb8;

  --panel:       #efe7dd;
  --panel-edge:  #cec1b2;
  --ink:         #1d1a17;
  --ink-soft:    #5a5049;
  --link:        #1b4b7a;

  --online:      #6fae3a;
  --offline:     #c0392b;
  --rule:        #24354a;

  --font-body: Georgia, "Times New Roman", serif;
  /* One UI stack for every small text: sidebars, lists, meta, forms.
     Nine places used to hardcode their own Tahoma variant, so the same
     kind of label rendered differently from block to block. */
  --font-ui:   Arial, "Helvetica Neue", Helvetica, sans-serif;
  /* ⚠ Cinzel is served from this box (assets/fonts), not from Google. The
     design was drawn against Trajan, which nobody has installed, so every
     heading silently fell back to Times. Cinzel is the same roman-capital
     shape and it actually arrives. */
  --font-head: "Cinzel", "Trajan Pro", "Times New Roman", serif;
}

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

html {
  /* Absorbs the crown's fixed overhang in the 1180-1260px band - see .menu.
     clip, not hidden: it never creates a scroll container. */
  overflow-x: clip; -webkit-text-size-adjust: 100%; }

/* ⚠ `hidden` must always win. Any rule that sets display (a .content-title
   is flex, a .field is block) outranks the browser's own [hidden] style, and
   the Services page showed an empty "Password change" heading that way. */
[hidden] { display: none !important; }

body {
  margin: 0;
  /* One reading face for the whole site, as on the reference server. The
     serif is kept for the About page's opening only (.opening). */
  font-family: var(--font-ui);
  font-size: 13px;
  color: var(--ink);
  /* One plate across the top, fading into the page colour.

     `cover` was wrong here: on a long page it scales the image to the whole
     document height, so you only ever see a zoomed-in sliver of it. Width is
     pinned to 100% and the height follows the aspect (2560x1440 -> 56.25vw),
     so the scene scales with the viewport but keeps its framing at any zoom.

     The gradient is sized to the same 56.25vw, so the fade always lands on the
     bottom of the picture rather than somewhere in the middle of the page. */
  background-color: var(--deep);
  background-image:
    /* sides: dissolve the plate's left and right edges into the page */
    linear-gradient(to right,
                    var(--deep) 0,
                    rgba(7, 13, 21, 0) 14%,
                    rgba(7, 13, 21, 0) 86%,
                    var(--deep) 100%),
    /* bottom: the same fade that is baked into the image, kept in sync */
    linear-gradient(rgba(7, 13, 21, 0) 0,
                    rgba(7, 13, 21, 0) 68%,
                    rgba(7, 13, 21, .55) 86%,
                    var(--deep) 100%),
    url("/assets/img/chaos/layout/fon-bg.webp");
  background-size: 100% 56.25vw, 100% 56.25vw, 100% auto;
  background-position: center top, center top, center top;
  background-repeat: no-repeat, no-repeat, no-repeat;
}

ul, ol { margin: 0; padding: 0; list-style: none; }
p { margin: 0; }
img { max-width: 100%; border: 0; }

a { color: var(--link); text-decoration: none; transition: color .18s ease, filter .18s ease; }
a:hover { filter: brightness(1.25); }

/* --------------------------------------------------------------- top bar -- */

.top-panel {
  position: absolute;
  width: 100%;
  height: 46px;
  background: rgba(3, 7, 12, .82);
  border-bottom: 1px solid rgba(226, 145, 63, .22);
  z-index: 20;
}

.top-panel-inner {
  width: min(1140px, 100% - 40px);
  height: 100%;
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
}

.top-links {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  font-family: var(--font-ui);
  font-size: 12px;
  min-width: 0;
}

.top-links a {
  color: var(--chrome);
  padding: 0 14px;
  border-right: 1px solid var(--rule);
  line-height: 1.2;
  white-space: nowrap;
}
.top-links a:first-child { padding-left: 0; }
.top-links a:last-child { border-right: 0; padding-right: 0; }
.top-links a:hover { color: var(--ember-hot); filter: none; }
/* The top bar carries reference pages, so it needs to say which one you are
   on - the crown below it does the same for the main bar. */
.top-links a.is-here { color: var(--ember); }

/* Real icons, not "YT"/"FB" text placeholders. Sized as squares so the row
   never shifts when one of them is missing. */
.top-social {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
}

.top-social a {
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border: 1px solid var(--rule);
  border-radius: 4px;
  color: var(--chrome-dim);
  background: rgba(255, 255, 255, .03);
  transition: color .18s ease, border-color .18s ease, background-color .18s ease;
}
.top-social a:hover {
  color: var(--ember-hot);
  border-color: var(--ember);
  background: rgba(226, 145, 63, .12);
  filter: none;
}
.top-social svg { display: block; }
/* A social slot with no address yet: same box, dimmed, and not a link.
   Same rule as the top links - a link that goes nowhere is worse than a
   label that says it is coming. */
.top-social .is-soon {
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border: 1px solid var(--rule);
  border-radius: 4px;
  opacity: .35;
  cursor: default;
  font-style: normal;
}

@media (max-width: 720px) {
  .top-panel { height: auto; padding: 6px 0; }
  .top-panel-inner { flex-direction: column; gap: 8px; }
  .top-links { justify-content: center; }
  .top-links a { padding: 2px 10px; }
}

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

/* Fluid, but capped: wider than the classic 1000px frame, still narrow
   enough that the parchment column does not read as stretched. */
.wrapper { width: min(1140px, 100% - 40px); margin: 0 auto; }

/* No background of its own on purpose. The page hero shows through, so the
   header can never disagree with the backdrop at any width.

   ⚠ The height is what its contents come to, NOT a fixed 518px. The menu bar
   keeps its own aspect ratio, so its height changes with the window: pinned
   at 518 the bar ran past the bottom of the header at wide sizes and sat on
   top of the panel instead of meeting it. With an automatic height the header
   always ends exactly where the bar ends, and the single negative margin on
   .bottom-container below is what sets the overlap. */
.header {
  position: relative;
}

.header > * { position: relative; z-index: 1; }

/* 74 + 200 = 274. The 64px freed by shrinking the logo is taken by the
   countdown below it, so the menu keeps its position.

   ⚠ The wordmark is a wide two-line lockup (aspect 2.04), not the old tall
   block, so the box is wider and shorter. padding-top absorbs the difference:
   107 + 167 = 274, the same band height the menu below is positioned against. */
.logo { padding-top: 107px; }

.logo a {
  display: block;
  width: 340px;
  height: 167px;
  margin: 0 auto;
  background: url("/assets/img/chaos/layout/logo-murealm.webp") center / contain no-repeat;
  filter: drop-shadow(0 6px 22px rgba(0, 0, 0, .7));
  transition: filter .25s ease;
}
.logo a:hover { filter: drop-shadow(0 6px 26px rgba(226, 145, 63, .45)) brightness(1.06); }

/* Menu sits on its own band so it reads against any hero art behind it. */
/* The menu IS the crown.

   Earlier these were two things: an ornate cap over the panel and a separate
   thin bar carrying the links. Stacked, they fought each other. They are one
   element now, built nine-slice so the ends and the centre gem keep their
   proportions while only the plain stretch between them repeats. */
/* One whole image, at its own aspect ratio.

   Every earlier attempt distorted it: stretching a 6.84:1 bar into a thin slot
   squashed the ornaments, and cropping to fit cut them off. Nothing is cut and
   nothing is stretched here. `aspect-ratio` lets the band keep its proportions
   at any width, so the art is used exactly as generated. */
.menu {
  position: relative;
  z-index: 6;                    /* over the panel: the slab slides underneath
                                    it, so the join is hidden by the bar
                                    itself and needs no cover piece */
  /* Runs 48px past the page frame on each side - the overhang is what makes
     the crown read as a drawn piece laid OVER the frame, not a bar inside it.
     ⚠ NO viewport units here. The old width was clamped with 100vw, and in
     the 1180-1260px window band - exactly where Chrome zoom lands - the
     crown changed size against the fixed frame and the whole top visibly
     shifted while zooming. Now every measure is fixed against the frame, so
     zoom cannot move anything; in that narrow band the end ornaments run a
     few px past the viewport instead, and html's overflow-x: clip quietly
     trims them - a static trim beats a sliding crown. Below 1180 the frame
     itself is fluid and the media override scales the overhang with it. */
  width: calc(100% + 96px);
  margin: 30px 0 0 -48px;
  /* ⚠ 14.16:1 is measured, not chosen. The end stones are drawn 0.60 as wide
     as they are tall, and this is the box that lands them exactly round -
     1.006. Any other height ovals them, so if the bar has to change height,
     the art has to be redrawn rather than squeezed further. */
  aspect-ratio: 1600 / 113;
  /* ⚠ 11% was drawn for seven items; the eighth pushed the last label out
     over the corner diamond. The links need the room, so the padding gives it
     back - keep this in step with the number of items in mainnav.ejs. */
  padding: 0 8.5%;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* ⚠ The bar art and its shadow live on this still layer, NOT on .menu.
   A filter on .menu itself had the animated stones and sparks inside it, so
   the browser re-ran a 24px drop-shadow over the whole 1200px bar on every
   frame of every spark: measured at 1.7 s of drawing per 4 s of an idle front
   page against 0.56 s this way. Same picture, the shadow just stays put. */
.menu::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: url("/assets/img/chaos/layout/menu-full.webp") center / 100% 100% no-repeat;
  filter: drop-shadow(0 10px 24px rgba(0, 0, 0, .6));
  pointer-events: none;
}

/* Three tracks: equal space on each side, the middle separator dead centre so
   it lands on the bar's own centre ornament. With 7 links the natural midpoint
   falls inside a link rather than between two, so the halves are placed
   explicitly (see head.ejs) and each half gets a 1fr track. */
/* ⚠ One row, centred, evenly spaced - NOT a 1fr/auto/1fr grid with a spacer
   in the middle. That layout stood two groups either side of the crown's
   centre ornament, and the bar art has not had one since it was redrawn; all
   it did was leave a 51px hole in the middle of the row. Every entry is now
   spaced by its own padding, so the gaps are equal by construction. */
.menu ul {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0;
  width: 100%;
}

.menu li { position: relative; display: block; }

/* ------------------------------------------------- the Jewel of Chaos -- */

/* The stones in the two end ornaments are the thing the server is named after,
   so they are lit rather than painted: a bloom that breathes over each stone
   and embers that drift up out of it.

   ⚠ The positions are measured off the bar art (gem centres at 2.70% and
   97.24% across, 47.5% down). Regenerate menu-full.webp and these three
   numbers have to be re-measured or the light lands beside the stone. */
.menu .mgem {
  position: absolute;
  top: 47.5%;
  width: 5.2%;                     /* the bloom is ~3x the stone it sits on */
  aspect-ratio: 1;
  margin: 0;
  pointer-events: none;
  z-index: 1;                      /* under .menu a, which is z-index 2 */
  background: radial-gradient(closest-side,
      rgba(255, 216, 146, .60) 0%,
      rgba(255, 152, 48, .36) 30%,
      rgba(255, 98, 16, .15) 55%,
      rgba(255, 80, 10, 0) 78%);
  mix-blend-mode: screen;          /* additive, so it reads as light not paint */
  animation: gemBreathe 3.4s ease-in-out infinite;
}
.menu .mgem-l { left: 2.70%; }
.menu .mgem-r { left: 97.24%; animation-delay: -1.7s; }

/* Each emitter is one dot plus three more in box-shadow, so two pseudo-elements
   throw eight embers without eight extra nodes.

   ⚠ The rise is in px, NOT %. A translateY percentage resolves against the
   element's own height, and these dots are 3px tall - as `-300%` the embers
   travelled nine pixels and looked like nothing was happening. */
.menu .mgem::before,
.menu .mgem::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 3px;
  height: 3px;
  margin: -1.5px 0 0 -1.5px;
  border-radius: 50%;
  background: rgba(255, 214, 158, 1);
  will-change: transform, opacity;
}
.menu .mgem::before {
  animation: gemEmberA 2.9s linear infinite;
  box-shadow:
    0 0 5px 1px rgba(255, 168,  70, .85),
    -9px  2px 0 -0.5px rgba(255, 178,  92, .95),
     8px -3px 0   -1px rgba(255, 214, 156, .90),
    -4px  6px 0 -0.5px rgba(255, 146,  52, .90);
}
.menu .mgem::after {
  animation: gemEmberB 4.1s linear infinite;
  animation-delay: -2.1s;
  box-shadow:
    0 0 5px 1px rgba(255, 150,  56, .80),
    11px  4px 0   -1px rgba(255, 192, 112, .90),
    -7px -4px 0 -0.5px rgba(255, 230, 182, .85),
     3px  7px 0   -1px rgba(255, 134,  40, .90);
}

@keyframes gemBreathe {
  0%, 100% { opacity: .70; transform: translate(-50%, -50%) scale(.92); }
  50%      { opacity: 1;   transform: translate(-50%, -50%) scale(1.10); }
}

/* Embers rise off the stone, drift sideways and burn out. Two tracks with
   different heights and durations so the eight sparks do not move as one lump. */
@keyframes gemEmberA {
  0%   { transform: translate(0, 0)          scale(.5);  opacity: 0; }
  10%  {                                                 opacity: 1; }
  55%  { transform: translate(4px, -22px)    scale(1);   opacity: .75; }
  100% { transform: translate(-3px, -46px)   scale(.25); opacity: 0; }
}
@keyframes gemEmberB {
  0%   { transform: translate(0, 0)          scale(.45); opacity: 0; }
  14%  {                                                 opacity: 1; }
  55%  { transform: translate(-5px, -27px)   scale(.9);  opacity: .7; }
  100% { transform: translate(4px, -58px)    scale(.2);  opacity: 0; }
}

/* Nobody needs a flickering navbar if they have asked for stillness. The
   stones stay lit, they just stop moving. */
@media (prefers-reduced-motion: reduce) {
  .menu .mgem { animation: none; opacity: .85; transform: translate(-50%, -50%); }
  .menu .mgem::before,
  .menu .mgem::after { animation: none; opacity: .7; }
  .menu li::before,
  .menu li::after { animation: none !important; opacity: 0; }
  /* The plate embers and blooms follow the same rule as the crown's. */
  .title-sidebar::before,
  .title-sidebar::after { animation: none !important; opacity: 0; }
  .hgem::before, .hgem::after { animation: none; opacity: 0; }
  /* The offline lamp stays lit red, it just stops beating. */
  .status-list li.is-offline::before { animation: none; opacity: .7; transform: none; }
  .is-down .herostrip-dot::after { animation: none; opacity: .7; transform: none; }
}

/* The links are spaced, not separated: there used to be a small ornament
   between every pair and a larger one in the middle, and on a bar that had a
   centre gem they read as clutter. Both are gone, along with the empty grid
   track that used to hold the halves apart. */

/* One set of metrics for every entry in the crown.

   ⚠ Links and "coming soon" labels MUST share this block. They used to carry
   their own padding and line-height, and the two drifted apart every time one
   of them was touched - a soon-label sat two pixels high in a row of links.
   Anything added to the menu goes in this selector list. */
.menu a,
.menu .is-soon {
  position: relative;
  z-index: 2;                      /* labels sit above the gem light, not under */
  display: block;
  /* ⚠ Was 6px 6px. The separators used to hold the links apart; with those
     gone the padding is the only thing doing it. */
  padding: 9px 15px 10px;
  font-family: var(--font-head);
  font-size: 13px;
  line-height: 1;
  letter-spacing: .04em;
  text-transform: uppercase;
  white-space: nowrap;
}

.menu a {
  color: var(--chrome);
  text-shadow: 0 2px 8px #000, 0 0 18px rgba(95, 127, 184, .35);
  transition: color .2s ease, text-shadow .2s ease;
}

/* The warm bloom behind a label, as if the stone under it were lit. */
.menu a::before {
  content: "";
  position: absolute;
  inset: 3px 7px 4px;
  border-radius: 3px;
  background: radial-gradient(ellipse at 50% 118%,
      rgba(226, 145, 63, .34) 0%,
      rgba(226, 145, 63, .12) 45%,
      rgba(226, 145, 63, 0) 72%);
  opacity: 0;
  transition: opacity .22s ease;
  pointer-events: none;
}

/* An ember rule that opens from the centre. Animating left/right rather than
   scaleX keeps the fade at each end from being stretched with it. */
.menu a::after {
  content: "";
  position: absolute;
  left: 50%;
  right: 50%;
  bottom: 3px;
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg,
      rgba(255, 192, 112, 0) 0%,
      var(--ember-hot) 22%,
      var(--ember-hot) 78%,
      rgba(255, 192, 112, 0) 100%);
  box-shadow: 0 0 9px rgba(255, 162, 62, .8);
  opacity: 0;
  transition: left .24s ease, right .24s ease, opacity .24s ease;
  pointer-events: none;
}

.menu a:hover,
.menu a:focus-visible {
  color: var(--ember-hot);
  text-shadow: 0 2px 8px #000, 0 0 20px rgba(255, 150, 50, .55);
  filter: none;                    /* the global a:hover brightness would double up */
}
.menu a:hover::before,
.menu a:focus-visible::before { opacity: 1; }
.menu a:hover::after,
.menu a:focus-visible::after { left: 11%; right: 11%; opacity: 1; }

/* The page you are on is lit, not merely coloured: the rule stays open, the
   bloom stays on, and the ember diamond from the icon set marks it. */
.menu a.is-active {
  color: var(--ember-hot);
  text-shadow: 0 2px 8px #000, 0 0 22px rgba(255, 150, 50, .6);
}
.menu a.is-active::before { opacity: .9; }
.menu a.is-active::after  { left: 9%; right: 9%; opacity: 1; }

/* The rule throws embers off both ends, the same idea as the stones in the
   ornaments. They hang off the <li> because the <a> has already spent
   ::before on the bloom and ::after on the rule itself. */
.menu li::before,
.menu li::after {
  content: "";
  position: absolute;
  bottom: 4px;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  background: rgba(255, 216, 162, 1);
  opacity: 0;
  pointer-events: none;
  z-index: 3;
}
.menu li::before {
  left: 11%;
  box-shadow:
    0 0 4px 1px rgba(255, 168, 70, .8),
    -4px -3px 0 -0.5px rgba(255, 186, 104, .9),
     1px -6px 0   -1px rgba(255, 224, 176, .8);
}
.menu li::after {
  right: 11%;
  box-shadow:
    0 0 4px 1px rgba(255, 168, 70, .8),
     4px -3px 0 -0.5px rgba(255, 186, 104, .9),
    -1px -6px 0   -1px rgba(255, 224, 176, .8);
}

.menu li:hover::before,
.menu li:focus-within::before,
.menu li.is-current::before { animation: edgeSparkL 1.5s linear infinite; }
.menu li:hover::after,
.menu li:focus-within::after,
.menu li.is-current::after  { animation: edgeSparkR 1.7s linear infinite; }
.menu li.is-current::after  { animation-delay: -.85s; }

/* Outward and up, because that is where the rule's own light is going. */
@keyframes edgeSparkL {
  0%   { transform: translate(0, 0)        scale(.5); opacity: 0; }
  18%  {                                              opacity: 1; }
  100% { transform: translate(-13px, -13px) scale(.2); opacity: 0; }
}
@keyframes edgeSparkR {
  0%   { transform: translate(0, 0)        scale(.5); opacity: 0; }
  18%  {                                              opacity: 1; }
  100% { transform: translate(13px, -13px)  scale(.2); opacity: 0; }
}

/* Told apart by tone, and it never lights up - there is nothing to go to. */
.menu .is-soon {
  font-style: normal;
  color: #67748a;
  text-shadow: 0 2px 8px #000;
  cursor: default;
}

/* The live strip, between the logo and the menu bar.

   This slot used to hold a countdown to opening day. The server is open, so
   the same space now carries what a visitor actually wants at a glance:
   whether it is up, how many are on, and the address to connect to. The
   numbers come from /api/status, so the header cannot disagree with the
   sidebar. Its height is what the header geometry was measured against. */
/* Under the logo, centred - the panel was tried at the frame's right edge
   and pulled back: it belongs beneath the logo. Dressed in the slab's own
   cloth - slab gradient, the silver frame line, and the sidebar title-plate
   art as its head - so it reads as one of the site's blocks standing in the
   hero, not as a web widget. With OPENING_DATE set it is the Grand Opening
   panel and the tiles hold the timer; empty, it is the live status strip. */
.herostrip {
  position: relative;
  z-index: 2;
  width: min(430px, 100% - 40px);
  margin: 12px auto 0;
  padding: 0 0 12px;
  text-align: center;
  background: linear-gradient(rgba(10, 18, 28, .93), rgba(7, 13, 21, .96));
  border: 1px solid rgba(141, 155, 176, .26);
  border-radius: 4px;
  box-shadow:
    inset 0 1px 0 rgba(214, 221, 232, .10),
    0 14px 30px rgba(0, 0, 0, .55);
}

/* The head IS a title plate - same art, same gems, as every sidebar block. */
.herostrip-head {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: calc(100% + 16px);
  margin: -5px -8px 4px;
  aspect-ratio: 800 / 182;
  background: url("/assets/img/chaos/layout/title-plate-top.webp") center / 100% 100% no-repeat;
  /* The plate's text band sits above its hanging scrollwork - lift the line
     onto it. */
  padding-bottom: 4.5%;
  font-family: var(--font-head);
  font-size: 14px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--chrome);
  text-shadow: 0 1px 0 #000;
}

/* The SAME glass lamp the Server Status rows carry - one lamp for the whole
   site, so up and down look identical everywhere. Red until the first
   /api/status answer flips it, exactly like the sidebar's default. */
.herostrip-dot {
  position: relative;
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
  background: url("/assets/img/chaos/layout/off-status.webp") center / contain no-repeat;
}
.is-up   .herostrip-dot { background-image: url("/assets/img/chaos/layout/on-status.webp"); }
.is-down .herostrip-head { color: var(--offline); }
/* Down beats like the sidebar lamp - same halo, same clock. */
.is-down .herostrip-dot::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 26px;
  height: 26px;
  margin: -13px 0 0 -13px;
  border-radius: 50%;
  background: radial-gradient(closest-side,
      rgba(255, 80, 60, .55) 0%,
      rgba(220, 50, 40, .28) 45%,
      rgba(190, 40, 30, 0) 75%);
  mix-blend-mode: screen;
  pointer-events: none;
  animation: lampPulse 1.8s ease-in-out infinite;
}

.herostrip-grid {
  display: grid;
  /* auto-fit: the status face has three tiles, the opening timer four -
     both fill the row without a dead column. */
  grid-template-columns: repeat(auto-fit, minmax(60px, 1fr));
  gap: 6px;
  padding: 0 12px;
}
.hs-cell {
  padding: 6px 2px 5px;
  background: rgba(4, 9, 16, .6);
  border: 1px solid rgba(141, 155, 176, .18);
  border-radius: 3px;
}
.hs-cell b {
  display: block;
  font-family: var(--font-head);
  font-size: 20px;
  line-height: 1.15;
  color: var(--chrome);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0 14px rgba(226, 145, 63, .35);
}
.hs-cell span {
  display: block;
  margin-top: 2px;
  font-family: var(--font-ui);
  font-size: 11px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--chrome-dim);
}

/* The hero plate's corner stones are full crown-stones: they breathe AND
   throw embers. Same bloom, same two ember tracks and four-spark shadow
   trick as .menu .mgem - the panel's gems are lit by the same fire. */
.herostrip-head { position: relative; }
/* ⚠ Sparks ONLY - the stones themselves stay as painted. A breathing bloom
   was tried here and on every plate stone and pulled the same day: "par
   daudz tas brush light". The gem is an anchor point for the embers, an
   invisible box, not a light source. */
.hgem {
  position: absolute;
  top: 35%;
  width: 38px;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  pointer-events: none;
}
.hgem-l { left: 6.9%; }
.hgem-r { left: 93.1%; animation-delay: -1.7s; }

.hgem::before,
.hgem::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 3px;
  height: 3px;
  margin: -1.5px 0 0 -1.5px;
  border-radius: 50%;
  background: rgba(255, 214, 158, 1);
  will-change: transform, opacity;
}
.hgem::before {
  animation: gemEmberA 2.9s linear infinite;
  box-shadow:
    0 0 5px 1px rgba(255, 168,  70, .85),
    -9px  2px 0 -0.5px rgba(255, 178,  92, .95),
     8px -3px 0   -1px rgba(255, 214, 156, .90),
    -4px  6px 0 -0.5px rgba(255, 146,  52, .90);
}
.hgem::after {
  animation: gemEmberB 4.1s linear infinite;
  animation-delay: -2.1s;
  box-shadow:
    0 0 5px 1px rgba(255, 150,  56, .80),
    11px  4px 0   -1px rgba(255, 192, 112, .90),
    -7px -4px 0 -0.5px rgba(255, 230, 182, .85),
     3px  7px 0   -1px rgba(255, 134,  40, .90);
}
.hgem-r::before { animation-delay: -1.4s; }
.hgem-r::after  { animation-delay: -3.1s; }

/* The opening timer wears the same tiles as the live counts, only the
   numbers speak louder - it is the one thing the panel is there to say. */
.hs-timer .hs-cell b { font-size: 24px; }
/* The grid rule above outranks the hidden attribute; live.js hides the
   tiles once opening day has passed. */
.hs-timer[hidden] { display: none; }
/* With the tiles gone the plate closes up under its head instead of
   leaving an empty pedestal. */
.herostrip.is-open { padding-bottom: 0; }
.herostrip.is-open .herostrip-head { margin-bottom: 0; }

.herostrip-addr {
  margin: 9px 12px 0;
  font-family: var(--font-ui);
  font-size: 11px;
  color: var(--chrome-dim);
}

/* The countdown to opening day, on its own line under the counts. */
.herostrip-open {
  display: flex;
  align-items: baseline;
  justify-content: center;
  flex-wrap: wrap;
  gap: 4px 10px;
  margin: 10px 12px 0;
  padding-top: 9px;
  border-top: 1px solid rgba(141, 155, 176, .22);
}
.ho-label {
  font-family: var(--font-ui);
  font-size: 11px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--chrome-dim);
}
.ho-clock {
  font-family: var(--font-head);
  font-size: 14px;
  letter-spacing: .04em;
  color: var(--chrome-dim);
  font-variant-numeric: tabular-nums;
}
/* The numbers carry the emphasis, the unit letters stay quiet - otherwise the
   line reads as one long string of characters. */
.ho-clock b {
  color: var(--ember-hot);
  font-weight: 500;
  text-shadow: 0 0 12px rgba(226, 145, 63, .35);
}
.herostrip-open.is-open .ho-label { color: var(--online); }

@media (max-width: 560px) {
  .herostrip { padding: 0 0 10px; }
  .herostrip-grid { grid-template-columns: repeat(2, 1fr); }
}

/* -------------------------------------------------------------- columns -- */

.middle { width: 100%; position: relative; }

.container, .top-container { background: none; }

/* ONE continuous slab.

   Previously each column was its own bordered box with a gap between them,
   so at every seam two borders sat side by side and read as a doubled edge.
   Now the dark tone and the outer border belong to the container, the columns
   are transparent, and the only line between them is the single rail on the
   content edge. */
.bottom-container {
  display: grid;
  grid-template-columns: 236px minmax(0, 1fr) 236px;
  gap: 0;
  align-items: stretch;
  /* Narrower than the wrapper on purpose: this is what lets the menu bar
     overhang the frame without ever leaving the viewport, which is what got
     its end ornaments clipped when the bar itself was oversized. */
  width: calc(100% - 48px);
  margin-left: auto;
  margin-right: auto;
  /* ⚠ THE OVERLAP, in one number.

     The header ends exactly where the menu bar ends, so pulling the panel up
     by this much slides the bar's lower half over the top of the slab. That
     join is what the design is built on: the bar closes the top of the frame
     instead of floating above it.

     It is a PERCENTAGE, and that is deliberate. A margin-top percentage is
     measured against the container's width, and the bar's height is its width
     over 6.84 - so one figure holds the same proportion of the bar at every
     window size. As a fixed 52px it was right at 1140 and swallowed the whole
     bar on a phone, where the bar is only 60px tall. 4.5% is the same overlap
     the art was drawn with. */
  margin-top: -4.5%;
  background:
    linear-gradient(rgba(10, 18, 28, .93), rgba(7, 13, 21, .96));
  border: 1px solid rgba(141, 155, 176, .26);
  border-radius: 4px;
  box-shadow:
    inset 0 1px 0 rgba(214, 221, 232, .10),
    0 -14px 26px rgba(0, 0, 0, .45),
    0 22px 60px rgba(0, 0, 0, .6);
  /* Deliberately NOT hidden: the corner brackets are meant to break the panel
     edge. Clipping them is what made the frame look like stacked boxes
     instead of one drawn piece. */
  overflow: visible;
  position: relative;
  z-index: 2;                      /* above the menu bar */
}

/* No cap here any more.

   The menu bar already closes the top of the frame. Stacking a second ornate
   moulding right under it put two heavy pieces on the same line, and they read
   as one broken shape rather than two. One statement per edge. */

.left-sidebar,
.right-sidebar {
  width: auto;
  min-width: 0;
  float: none;
  /* ⚠ 68px tucks the first title plate 2px UNDER the crown's painted
     bottom edge (measured off menu-full.webp: the paint in the plate zones
     ends 46.7px into the slab, the art box at 51; plate top = 68 - 23 = 45).
     Zero visible strip at any zoom - the dark band between the bar and the
     plates was the "hole across the whole width". Only these 2px may hide:
     any deeper and the plate reads as shoved under the menu. */
  padding: 68px 18px 34px;
  color: var(--chrome-dim);
  font-size: 12px;
  background: none;              /* the slab provides it */
  border: 0;
  box-shadow: none;
}
.left-sidebar  { grid-column: 1; grid-row: 1; }
.right-sidebar { grid-column: 3; grid-row: 1; }

/* The parchment column. Its side rails are the only division in the slab. */
.content {
  position: relative;
  grid-column: 2;
  grid-row: 1;
  margin: 0;
  /* 56px keeps the parchment's first heading in step with the sidebar
     plates (see .left-sidebar's 68px note) - one rhythm across the slab. */
  padding: 56px 34px 46px;
  min-width: 0;
  /* No tiling.

     The parchment used to repeat every 512px and the tile seams read as a grid
     of squares down the column. The sheet is now seamless top-to-bottom, so it
     tiles vertically with no visible line, and the aging comes from side
     shading only: nothing darkens the top or bottom edge. */
  background-color: var(--panel);
  background-image:
    linear-gradient(rgba(206, 193, 178, .5), rgba(206, 193, 178, 0) 24px),
    linear-gradient(90deg,
      rgba(176, 158, 134, .40) 0,
      rgba(176, 158, 134, .10) 26px,
      rgba(176, 158, 134, 0) 70px,
      rgba(176, 158, 134, 0) calc(100% - 70px),
      rgba(176, 158, 134, .10) calc(100% - 26px),
      rgba(176, 158, 134, .40) 100%),
    url("/assets/img/chaos/layout/paper.webp");
  background-size: auto, auto, 100% auto;
  background-repeat: no-repeat, no-repeat, repeat-y;
  background-position: top, top, center top;
  border-left: 1px solid rgba(141, 155, 176, .5);
  border-right: 1px solid rgba(141, 155, 176, .5);
  /* the generated rail, tiled down both seams */
  background-position: 0 0, center top;
  box-shadow:
    inset  6px 0 14px -8px rgba(0, 0, 0, .55),
    inset -6px 0 14px -8px rgba(0, 0, 0, .55),
    inset 0 0 0 2px rgba(255, 255, 255, .28);

  /* ⚠ The parchment runs the full height of the slab, as on the reference
     server. It used to end where its text ended (align-self: start), which
     suited the old per-page sidebars; since 2026-10-05 every page carries
     the same two columns, and a sheet stopping halfway down them left a
     dark hole under every short page. */
  align-self: stretch;
  min-height: 460px;
  border-bottom: 1px solid rgba(141, 155, 176, .5);
  margin-bottom: 0;
}



/* ------------------------------------------------------- sidebar blocks -- */

.block { margin-bottom: 26px; }
.block:last-child { margin-bottom: 0; }

/* ----------------------------------------------- the heading plate, once -- */

/* ONE plate for every block heading in the sidebars.

   There used to be two drawings in a column: this quiet one on the first block
   and an ornate one - filigree clusters gripping a big stone at each end - on
   all the rest. The ornate plate has been taken out of the headings entirely
   and left to the login button, which is the only thing in the column you are
   meant to click. With two ornate drawings the headings competed with the
   button and the button lost, being the smaller piece; with one, the button is
   the only ornament in the panel and the eye lands on it.

   The drawing is a plain rectangle: dead flat top edge, straight sides, a
   simple bottom rail with a filigree hem under it, and two small amber studs
   set at 5.75% / 94.25% across. Nothing on it flares, which is what lets the
   first block in a column butt straight against the menu bar.

   ⚠ Geometry, not taste:
     +36/-18 spans the plate across the column edge to edge (the column pads by
             18px), so every heading's straight sides land on the same two
             lines down the slab.
     4.396:1 is the art's own ratio. aspect-ratio, NOT a fixed height: the
             plate is one drawn piece, so the box has to take the picture's
             proportion or the studs and the hem stretch - and an aspect
             follows the sidebar through its breakpoints, where a fixed height
             did not. Re-measure if title-plate-top.webp is redrawn. */
/* Embers off the plate stones.

   Every title plate carries the two small orange gems its art was painted
   with, and they spark the same way the crown's end stones do - the SAME
   ember keyframes and the same four-spark box-shadow trick, so the plates
   read as lit by the same fire. Measured off title-plate-top.webp: the gems
   sit at 6.9% and 93.1% across, 35% down.
   ⚠ Sparks only - no bloom, no glow line. The wide seam glow was tried and
   pulled: the joint stays clean, the stones alone are alive. */
.title-sidebar::before,
.title-sidebar::after {
  content: "";
  position: absolute;
  top: 35%;
  width: 3px;
  height: 3px;
  margin: -1.5px 0 0 -1.5px;
  border-radius: 50%;
  background: rgba(255, 214, 158, 1);
  pointer-events: none;
  will-change: transform, opacity;
}
.title-sidebar::before {
  left: 6.9%;
  animation: gemEmberA 2.9s linear infinite;
  box-shadow:
    0 0 5px 1px rgba(255, 168,  70, .85),
    -9px  2px 0 -0.5px rgba(255, 178,  92, .95),
     8px -3px 0   -1px rgba(255, 214, 156, .90),
    -4px  6px 0 -0.5px rgba(255, 146,  52, .90);
}
.title-sidebar::after {
  left: 93.1%;
  animation: gemEmberB 4.1s linear infinite;
  animation-delay: -2.1s;
  box-shadow:
    0 0 5px 1px rgba(255, 150,  56, .80),
    11px  4px 0   -1px rgba(255, 192, 112, .90),
    -7px -4px 0 -0.5px rgba(255, 230, 182, .85),
     3px  7px 0   -1px rgba(255, 134,  40, .90);
}
/* Neighbouring plates must not spark in unison - a page of blocks pulsing
   on one clock reads as a screensaver. Every second and third block starts
   its fire at a different point in the loop. */
.block:nth-child(2n) .title-sidebar::before { animation-delay: -1.3s; }
.block:nth-child(2n) .title-sidebar::after  { animation-delay: -3.2s; }
.block:nth-child(3n) .title-sidebar::before { animation-delay: -0.6s; }
.block:nth-child(3n) .title-sidebar::after  { animation-delay: -1.7s; }

.title-sidebar {
  position: relative;
  width: calc(100% + 36px);
  margin-left: -18px;
  aspect-ratio: 800 / 182;
  /* ⚠ The box includes the scrollwork hanging under the rail - about 8.6px of
     it at a 232px column - so the usual 12px would push the block's content
     two gaps away from the heading. 4px puts it back level with every other
     block.

     ⚠ Then it went back up. 4px put the first row of content directly under
     the tip of the hanging scrollwork, which is what made the Server Status
     row and the Sign In fields read as cramped. 14px is the ornament's 8.6px
     drop plus the breathing room every other block gets under its plate. */
  margin-bottom: 14px;
  /* ⚠ And the same hem is why the heading needs this. `place-items: center`
     centres in the whole box, hem included, so "Server Status" and "Sign In"
     sat low on the plate. The rail is at 84.07% of the art's height; padding
     out the remaining 15.93% makes the grid centre on the FACE instead.
     It is a percentage because padding percentages resolve against the
     column's width, which is what the plate scales with - a fixed px value
     would drift at the narrower breakpoints. */
  padding-bottom: 4.3%;
  background: url("/assets/img/chaos/layout/title-plate-top.webp") center / 100% 100% no-repeat;
  display: grid;
  place-items: center;
}

/* The first block in each column does not sit BELOW the header, it hangs OFF
   it, and -23px is what closes the gap: the sidebar's 64px top padding minus
   the 41px at which the menu bar's underside actually sits over a side column.
   It follows from the bar art - redraw the bar and re-derive it. Everything
   else about this plate is now the same as every other heading's, which is why
   this rule is one line. */
.left-sidebar  > .block:first-child .title-sidebar,
.right-sidebar > .block:first-child .title-sidebar {
  margin-top: -23px;
}

/* ⚠ ONE colour for every block title on the plate.

   There were two: live blocks in the bright chrome and "not yet" blocks in
   the dim one, which made the sidebar read as two different designs stacked
   on each other. The dimmer tone wins - on a dark plate it is what the rest
   of the column is written in, and the plate art is already doing the work of
   marking the title out. */
.block-title {
  font-family: var(--font-head);
  /* ⚠ Was 11.5px, and at that size the heading floated in the middle of the
     plate with slate all around it - which read as the plate being too heavy
     rather than the type being too small. The plate is sized for this. */
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--chrome-dim);
  text-shadow: 0 2px 10px #000, 0 0 14px rgba(226, 145, 63, .3);
  /* clears the stones, which end at 9.2% / 90.8% of the plate's width */
  padding: 0 28px;
  text-align: center;
  line-height: 1.25;
  max-width: 100%;
}

.sub-note {
  font-family: var(--font-ui);
  font-size: 11px;
  color: var(--chrome-dim);
  text-align: center;
  margin-bottom: 14px;
}

.more-link { text-align: center; margin-top: 12px; }
.more-link a, .link-warm {
  font-family: var(--font-ui);
  font-size: 12px;
  color: var(--ember);
}
.more-link a:hover, .link-warm:hover { color: var(--ember-hot); filter: none; }

/* server status */

.status-list { font-family: var(--font-ui); padding-bottom: 4px; }

.status-list li {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 9px;
  /* ⚠ The lamp art is 32px so it stays crisp on a dense screen; background-size
     is what actually draws it, and the gutter is measured from that. Change one
     and change the other.

     ⚠ min-height is not optional. A background is clipped to the border box,
     and this row is only as tall as its 12px line - 14.4px - so a 16px lamp
     lost 0.8px off the top AND the bottom and read as a circle with its ends
     sliced off. 18px leaves a pixel of clearance. Whatever the lamp is drawn
     at, this has to stay larger than it. */
  min-height: 18px;
  padding-left: 22px;
  background-size: 16px 16px;
  font-size: 12px;
  background-position: left center;
  background-repeat: no-repeat;
}
.status-list li.is-online  { background-image: url("/assets/img/chaos/layout/on-status.webp"); }
/* ⚠ off-status.webp is the SAME lamp as on-status with its red and green
   channels swapped (tools: PIL channel swap) - same glass, same highlight,
   red light. The old asset was a dull grey pebble that read as "broken",
   not as "down". */
.status-list li.is-offline { background-image: url("/assets/img/chaos/layout/off-status.webp"); }

/* A downed server throbs. The red lamp carries a soft pulsing halo - light,
   not paint, centred on the lamp itself; the green lamp stays calm, steady
   is what "up" looks like. */
.status-list li { position: relative; }
.status-list li.is-offline::before {
  content: "";
  position: absolute;
  left: 8px;                       /* lamp centre: 16px lamp at left edge */
  top: 50%;
  width: 26px;
  height: 26px;
  margin: -13px 0 0 -13px;
  border-radius: 50%;
  background: radial-gradient(closest-side,
      rgba(255, 80, 60, .55) 0%,
      rgba(220, 50, 40, .28) 45%,
      rgba(190, 40, 30, 0) 75%);
  mix-blend-mode: screen;
  pointer-events: none;
  animation: lampPulse 1.8s ease-in-out infinite;
}

@keyframes lampPulse {
  0%, 100% { opacity: .35; transform: scale(.75); }
  50%      { opacity: 1;   transform: scale(1.15); }
}

/* The header band's dot says the same thing - it beats to the same pulse. */
.is-down .herostrip-dot { animation: lampPulse 1.8s ease-in-out infinite; }

/* ⚠ min-width:0 is what lets a flex child shrink below its text width. Without
   it a longer SERVER_LABEL pushes the online count out of the column instead of
   ellipsing, and the label is set from the environment - it can be anything. */
.status-list a, .status-list li > span:not(.count) {
  color: var(--chrome-dim);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.status-list a:hover { color: var(--ember-hot); filter: none; }
.status-list .count { margin-left: auto; font-weight: 700; }
.is-online  .count { color: var(--online); }
.is-offline .count { color: var(--offline); }

.status-totals {
  border-top: 1px solid var(--rule);
  padding-top: 12px;
  font-family: var(--font-ui);
  font-size: 12px;
  line-height: 22px;
}
.status-totals div { display: flex; justify-content: space-between; }
.status-totals .val { color: var(--online); font-weight: 700; }

/* clock + events */

.clock {
  text-align: center;
  font-family: var(--font-ui);
  font-size: 17px;
  letter-spacing: .08em;
  color: var(--chrome-dim);
  text-shadow: 0 0 14px rgba(226, 145, 63, .28);
  margin-bottom: 4px;
}

.event { margin-bottom: 9px; font-family: var(--font-ui); }
.event + .event { border-top: 1px solid var(--rule); padding-top: 9px; }

.event-title { display: flex; justify-content: space-between; color: var(--chrome-dim); font-size: 12px; }
.event-info  { display: flex; justify-content: space-between; color: #6b7788; font-size: 11px; margin-top: 3px; }
.event-info .hot   { color: var(--ember); }
.event-info .warn  { color: var(--offline); }
.event-info .go    { color: var(--online); }

/* generic list rows (topics, lottery, market, forum) */

.row {
  border-bottom: 1px solid var(--rule);
  padding-bottom: 9px;
  margin-bottom: 9px;
}
.row:last-child { border-bottom: 0; }

.row-title a { color: var(--chrome-dim); font-size: 12px; }
.row-title a:hover { color: var(--ember-hot); filter: none; }
.row-meta { color: #6b7788; font-size: 11px; margin-top: 2px; font-family: var(--font-ui); }
.row-meta a { color: var(--chrome-dim); }

.row-flex { display: flex; justify-content: space-between; gap: 8px; }
.row-price { color: var(--online); font-size: 11px; white-space: nowrap; font-family: var(--font-ui); }

/* item rarity colours */
.g-exc  { color: #6ddc8a; }
.g-anc  { color: #7f9bff; }
.g-wing { color: var(--ember); }
.g-pet  { color: #d8a7ff; }

/* tabs used by treasury / winners */

.tabs { text-align: center; margin-top: 12px; border-top: 1px solid var(--rule); padding-top: 10px; }
.tabs button {
  background: none; border: 0; cursor: pointer;
  font-family: var(--font-ui); font-size: 11px;
  color: var(--chrome-dim);
  padding: 3px 6px;
}
.tabs button:hover { color: var(--chrome); }
.tabs button.is-active { color: var(--ember); text-decoration: underline; }

.tab-panel { display: none; animation: fade .35s both; }
.tab-panel.is-active { display: block; }
@keyframes fade { from { opacity: 0; transform: scale(.97); } to { opacity: 1; transform: none; } }

/* login */

.login { padding-top: 2px; }

/* One column, one rhythm.

   Fields are plain CSS now. The generated slot art had to be stretched to the
   column width and its bevel broke every time that width changed, which is why
   they looked cropped. The button below keeps its art - it is drawn to a ratio,
   not to a height, so the column width can move without breaking it. */
#acLoginForm {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 9px;
}

.login-field { width: 100%; }

.login-field input,
.login select {
  width: 100%;
  height: 38px;
  padding: 0 12px;
  background: linear-gradient(rgba(4, 9, 16, .92), rgba(12, 22, 34, .92));
  border: 1px solid rgba(141, 155, 176, .34);
  border-radius: 3px;
  box-shadow:
    inset 0 1px 3px rgba(0, 0, 0, .7),
    inset 0 -1px 0 rgba(214, 221, 232, .06);
  color: var(--chrome);
  font-family: var(--font-ui);
  font-size: 12px;
  text-align: center;
  outline: none;
  transition: border-color .18s ease, box-shadow .18s ease;
}
.login-field input::placeholder { color: #63728a; }

.login-field input:focus,
.login select:focus {
  border-color: var(--ember);
  box-shadow:
    inset 0 1px 3px rgba(0, 0, 0, .7),
    0 0 0 2px rgba(226, 145, 63, .18);
}

.login select {
  text-align-last: center;
  appearance: none;
  cursor: pointer;
  /* caret drawn in CSS so it never depends on a sprite */
  background-image:
    linear-gradient(45deg, transparent 50%, var(--chrome-dim) 50%),
    linear-gradient(135deg, var(--chrome-dim) 50%, transparent 50%),
    linear-gradient(rgba(4, 9, 16, .92), rgba(12, 22, 34, .92));
  background-position:
    calc(100% - 16px) calc(50% - 2px),
    calc(100% - 11px) calc(50% - 2px),
    0 0;
  background-size: 5px 5px, 5px 5px, 100% 100%;
  background-repeat: no-repeat;
}
.login select option { background: var(--navy); color: var(--chrome); }

/* The submit button is one drawn piece out of the same set as the title plates:
   pewter frame, slate face, a round amber stone in a filigree cluster at each
   end. Two numbers keep it honest.

   aspect-ratio, not height. The art is 800x216 and the box is given that exact
   ratio, so the button scales by width alone and the two stones stay circles.
   The button this replaces was drawn 2:1 and forced into a 44px bar - a 4.5x
   horizontal squeeze that flattened both stones into ovals.

   26px of side padding. A gem cluster is 17% of the button's width, so at the
   140px cap it measures 24px - the label can never crawl onto a stone. */
.login button {
  display: block;
  width: 100%;
  aspect-ratio: 800 / 216;
  /* ⚠ The cap is what keeps this a button rather than a third plate.

     At the column's full 200px it stood 54px tall - taller than the heading
     above it - and being the same family of drawing as the old ornate heading
     plate, it read as a heading rather than as something to press. Stepped in
     to 140px it is 38px tall: well inside the fields it submits and two thirds
     the height of the plate above, so the panel reads top-down as heading,
     fields, act. What makes it the loudest thing in the panel is its lit amber
     label against the headings' dim chrome, not its size.

     The cap also has to be here for the phone. Below 860px the sidebars reflow
     into 230px-and-wider auto-fit columns, and a ratio box with nothing holding
     it back grew the button to 80px tall. */
  max-width: 140px;
  margin: 8px auto 0;
  padding: 0 26px;
  border: 0;
  cursor: pointer;
  background: url("/assets/img/chaos/layout/login-button.webp") center / 100% 100% no-repeat;
  /* The face is dark stone now, so the label is lit rather than stamped. */
  color: var(--ember-hot);
  font-family: var(--font-head);
  font-size: 13px;
  letter-spacing: .08em;
  text-transform: uppercase;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .8), 0 0 10px rgba(226, 145, 63, .35);
  transition: filter .18s ease, text-shadow .18s ease, transform .08s ease;
}
/* Hover pushes the stones' glow into the label: the plate itself is a fixed
   drawing, so the lit text is the only part that can carry the state. */
.login button:hover {
  filter: brightness(1.13) saturate(1.06);
  text-shadow: 0 1px 2px rgba(0, 0, 0, .8), 0 0 16px rgba(255, 192, 112, .75);
}
.login button:active { transform: translateY(1px); filter: brightness(.94); }
.login button:focus-visible {
  outline: none;
  filter: brightness(1.13);
  text-shadow: 0 1px 2px rgba(0, 0, 0, .8), 0 0 16px rgba(255, 192, 112, .9);
}

.login-links {
  text-align: center;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--rule);
  line-height: 21px;
}
.login-links a { color: var(--ember); font-size: 12px; font-family: var(--font-ui); }
.login-links a:hover { color: var(--ember-hot); filter: none; }

/* the signed-in account panel (painted by account.js) */

.account-panel { font-family: var(--font-ui); }

.ac-facts { font-size: 11.5px; margin-bottom: 12px; }
.ac-facts li {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 4px 0;
  border-bottom: 1px solid rgba(36, 53, 74, .7);
}
.ac-facts li:last-child { border-bottom: 0; }
.ac-facts span { color: var(--chrome-dim); }
.ac-facts em { color: var(--chrome); font-style: normal; text-align: right; }
.ac-vip { color: var(--ember); font-weight: 700; }
.ac-on  { color: var(--online); }

.ac-menu {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--rule);
  padding-top: 8px;
}
.ac-item {
  padding: 5px 8px;
  font-size: 12px;
  color: var(--chrome-dim);
  border-left: 2px solid transparent;
}
a.ac-item:hover { color: var(--ember-hot); border-left-color: var(--ember); filter: none; }
a.ac-item.is-active { color: var(--chrome); border-left-color: var(--ember); background: rgba(226, 145, 63, .08); }

.ac-out {
  width: 100%;
  margin-top: 12px;
  padding: 9px 0;
  cursor: pointer;
  border: 1px solid rgba(141, 155, 176, .3);
  border-radius: 3px;
  background: rgba(255, 255, 255, .04);
  color: var(--chrome-dim);
  font-family: var(--font-ui);
  font-size: 12px;
  letter-spacing: .04em;
}
.ac-out:hover { color: var(--chrome); border-color: var(--chrome-dim); }

.ac-err {
  margin: 2px 0 4px;
  padding: 7px 9px;
  font-size: 11px;
  line-height: 1.45;
  color: #ffb4a8;
  background: rgba(192, 57, 43, .16);
  border: 1px solid rgba(192, 57, 43, .45);
  border-radius: 3px;
}

/* ranked lists: top players, top guilds */

.rank-list { font-family: var(--font-ui); font-size: 12px; }
.rank-list li {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 6px 0;
  border-bottom: 1px solid rgba(36, 53, 74, .7);
}
.rank-list li:last-child { border-bottom: 0; }

/* Fixed width so the names line up however many digits the rank has. */
.rank-list .rk {
  flex: none;
  width: 17px;
  height: 17px;
  display: grid;
  place-items: center;
  border-radius: 2px;
  background: rgba(255, 255, 255, .05);
  border: 1px solid rgba(141, 155, 176, .22);
  font-size: 11px;
  color: var(--chrome-dim);
}
/* ⚠ No metals in the sidebar. Gold, silver and bronze on the first three
   rows put three more colours into a fifteen-block column that is meant to
   read as one material. The top three are marked by the ember edge alone;
   the medals stay on the rankings page, where the table is the subject. */
.rank-list li:nth-child(-n+3) .rk {
  color: var(--ember);
  border-color: rgba(226, 145, 63, .45);
}

.rank-list a {
  color: var(--chrome-dim);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 6px;
}
.rank-list a:hover { color: var(--ember-hot); filter: none; }
.rank-list em { font-style: normal; color: var(--chrome-dim); font-size: 11px; white-space: nowrap; }

.live-empty { color: #61708a; font-family: var(--font-ui); font-size: 11.5px; font-style: italic; }

/* The guild emblem: 8x8 of the game's own packed pixels. */
.gmark {
  flex: none;
  display: grid;
  grid-template-columns: repeat(8, 2px);
  grid-auto-rows: 2px;
  gap: 0;
  outline: 1px solid rgba(0, 0, 0, .6);
}
.gmark i { display: block; width: 2px; height: 2px; }
.gmark-none { width: 16px; height: 16px; background: rgba(255, 255, 255, .05); }

/* castle siege */

.siege { font-family: var(--font-ui); text-align: center; margin-bottom: 12px; }
.siege-label { font-size: 11px; color: var(--chrome-dim); }
.siege-guild {
  display: block;
  margin: 4px 0 2px;
  font-family: var(--font-head);
  font-size: 15px;
  color: var(--ember);
  text-shadow: 0 0 14px rgba(226, 145, 63, .3);
}
.siege-lord { font-size: 11px; color: var(--chrome-dim); }

/* reset gates and rules */

.rs-gates { font-family: var(--font-ui); font-size: 11.5px; }
.rs-gates li {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 5px 0;
  border-bottom: 1px solid rgba(36, 53, 74, .7);
  color: var(--chrome-dim);
}
.rs-gates li:last-child { border-bottom: 0; }
.rs-gates b { color: var(--chrome); font-weight: 400; }
.rs-gates em { margin-left: auto; font-style: normal; }
.rs-gates .is-open { color: var(--online); }
.rs-gates .is-shut { color: var(--chrome-dim); }

.rs-rules { font-family: var(--font-ui); font-size: 11.5px; line-height: 1.5; }
.rs-rules li {
  padding: 7px 0;
  border-bottom: 1px solid rgba(36, 53, 74, .7);
  color: var(--chrome-dim);
}
.rs-rules li:last-child { border-bottom: 0; }
.rs-rules b { color: var(--chrome); }

/* discord + download */

.discord { font-family: var(--font-ui); font-size: 12px; text-align: center; }

/* Icon sits inside the plate, on the same baseline as the label. */
.block-title-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
.block-title-icon svg {
  flex: none;
  color: #8b9bf7;                 /* Discord blurple, toned for our palette */
  filter: drop-shadow(0 1px 0 #000);
}
.discord .members { display: flex; align-items: center; gap: 8px; justify-content: center; margin-bottom: 12px; }
.discord .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--online); }

.btn-outline {
  display: inline-block;
  padding: 6px 18px;
  border: 1px solid var(--rule);
  border-radius: 4px;
  color: var(--chrome);
  font-family: var(--font-ui);
  font-size: 12px;
  background: rgba(255, 255, 255, .04);
}
.btn-outline:hover { border-color: var(--ember); color: var(--ember-hot); filter: none; }

/* Grows with its content.

   This used to sit on a fixed 148x143 sprite. The text needed more room than
   the art had, so the caption spilled straight over the picture. The frame is
   CSS now, so the box follows the content instead of the content fighting a
   fixed-size background. */
.download {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 16px 12px 18px;
  text-align: center;
  font-family: var(--font-ui);
  font-size: 11px;
  line-height: 1.45;
  color: var(--chrome-dim);
  background: linear-gradient(rgba(10, 18, 28, .55), rgba(7, 13, 21, .75));
  border: 1px solid rgba(141, 155, 176, .22);
  border-radius: 3px;
  box-shadow: inset 0 1px 0 rgba(214, 221, 232, .07);
}
.download p { margin: 0; }
.download img {
  display: block;
  margin: 2px 0;
  transition: filter .2s ease, transform .2s ease;
}
.download a:hover img { filter: brightness(1.2); transform: translateY(-1px); }
.download a { color: var(--ember); font-size: 12px; }
.download a:hover { color: var(--ember-hot); filter: none; }

/* treasury */

.treasury { font-family: var(--font-ui); font-size: 11px; }
.treasury div { display: flex; justify-content: space-between; margin-bottom: 7px; }
.treasury .val { color: var(--chrome); }

/* quest winners */

.quest-list { text-align: center; font-family: var(--font-ui); margin: 10px 0; }
.quest-list a { color: var(--chrome-dim); font-size: 12px; display: block; }
.quest-list p  { color: #6b7788; font-size: 11px; margin: 8px 0; }

/* --------------------------------------------------------- main content -- */

/* Quiet on purpose.

   Ornament belongs to the dark frame: menu, corners, sidebar plates. When
   the light column carried the same heavy metal bars, every element competed
   and the page read as noise. In here a title is a thin ruled band with dark
   text, so the eye goes to the content. */
.content-title {
  position: relative;
  width: 100%;
  height: 34px;
  padding: 0 16px;
  background: url("/assets/img/chaos/layout/title-paper.webp") center / cover no-repeat;
  border: 1px solid rgba(141, 155, 176, .45);
  border-left: 3px solid var(--ember);
  border-radius: 2px;
  box-shadow: 0 1px 0 rgba(255, 255, 255, .5) inset, 0 2px 8px rgba(0, 0, 0, .10);
  font-family: var(--font-head);
  font-size: 14px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: #34302b;
  text-shadow: 0 1px 0 rgba(255, 255, 255, .6);

  display: flex;
  align-items: center;
  justify-content: flex-start;
}

.content-title .aside {
  position: absolute;
  right: 14px;
  top: 50%;
  transform: translateY(-50%);
  font-family: var(--font-ui);
  font-size: 11px;
  text-transform: none;
  letter-spacing: 0;
  white-space: nowrap;
}
/* ⚠ No bullet on these. The heading's aside is used for a back link, a count
   and a pair of view buttons as well as "see more", and the little plus was
   turning up in front of all of them ("+ <- Rankings"). */
.content-title .aside a { color: #8a4a1e; text-shadow: none; }
.content-title .aside a:hover { color: var(--ember); filter: none; }

.content-block { padding: 18px 0 26px; font-family: var(--font-ui); font-size: 12px; color: var(--ink); }

/* announcements */

.announce { display: flex; gap: 10px; align-items: flex-start; margin-bottom: 12px; font-size: 13px; line-height: 1.5; }
.announce img { margin-top: 2px; }

/* news */

.news { padding-bottom: 12px; border-bottom: 1px solid var(--panel-edge); margin-bottom: 22px; }
.news:last-child { border-bottom: 0; }

.news-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.news-head h2 { margin: 0; font-size: 17px; font-weight: normal; }
.news-head h2 a { color: var(--link); }
.news-date { color: var(--ink-soft); font-size: 12px; white-space: nowrap; }

.news-body {
  margin-top: 12px;
  font-family: var(--font-ui);
  font-size: 12px;
  line-height: 18px;
  color: #14110f;
}
.news-body p + p { margin-top: 10px; }

.news-foot {
  margin-top: 12px;
  display: flex;
  justify-content: space-between;
  color: var(--ink-soft);
  font-size: 12px;
}
.news-foot a { color: #8a3a20; }

/* carousel */

/* No gutters: the gallery runs the full column width so its edges line up with
   the heading bars above it. The arrows are pulled out into the column padding
   instead, where there is already empty space. */
.carousel { position: relative; padding: 0 36px; }

.gallery { width: 100%; overflow: hidden; }
.gallery ul { display: flex; gap: 10px; transition: transform .3s ease; }

/* Frame is drawn, not pasted. A generated PNG laid over the photo always left
   a faint rectangular imprint at the edges, because diffusion never gives a
   perfectly clean alpha. Borders and shadows are exact at any size. */
.gallery li {
  /* Exactly four across whatever width the column has, so a tile is never
     sliced in half at the right edge. */
  flex: 0 0 calc((100% - 30px) / 4);
  aspect-ratio: 4 / 3;
  height: auto;
  position: relative;
  border: 1px solid rgba(141, 155, 176, .75);
  border-radius: 2px;
  background: var(--navy);
  box-shadow: 0 2px 10px rgba(0, 0, 0, .35);
  overflow: hidden;
  transition: border-color .2s ease, box-shadow .2s ease, transform .2s ease;
}
.gallery li a { display: block; width: 100%; height: 100%; }
.gallery li img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.gallery li:hover {
  border-color: var(--ember);
  box-shadow:
    inset 0 0 0 1px rgba(7, 13, 21, .9),
    0 4px 18px rgba(226, 145, 63, .28);
  transform: translateY(-1px);
}

/* The arrows have a lane of their own inside the content (the carousel's
   side padding), as tall as the pictures: they used to hang 30px past the
   parchment into the side columns and stick out above and below the row. */
.arrow {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 30px;
  opacity: .92;
  border: 0;
  padding: 0;
  cursor: pointer;
  z-index: 3;
  background: transparent no-repeat center / contain;
  transition: filter .2s ease;
}
.arrow:hover { filter: brightness(1.3); opacity: 1; }
.arrow.prev { left: 0;  background-image: url("/assets/img/chaos/layout/nav-left.webp"); }
.arrow.next { right: 0; background-image: url("/assets/img/chaos/layout/nav-right.webp"); }

/* generic light table used on stats / rankings pages */

.sheet {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-family: var(--font-ui);
  font-size: 12px;
  border: 1px solid rgba(141, 155, 176, .55);
  border-radius: 3px;
  overflow: hidden;
  box-shadow: 0 6px 22px rgba(0, 0, 0, .18);
}
.sheet th {
  background: linear-gradient(#22384f, #12202f 60%, #0c1725);
  color: var(--chrome);
  font-family: var(--font-head);
  font-size: 12px;
  letter-spacing: .06em;
  text-transform: uppercase;
  text-align: left;
  padding: 10px 12px;
  border-bottom: 1px solid var(--ember);
  text-shadow: 0 1px 0 #000;
}
.sheet td {
  padding: 9px 12px;
  border-bottom: 1px solid rgba(206, 193, 178, .8);
  background: rgba(255, 255, 255, .34);
  color: var(--ink);
}
.sheet tr:nth-child(even) td { background: rgba(206, 193, 178, .28); }
.sheet tr:last-child td { border-bottom: 0; }
.sheet tbody tr:hover td { background: rgba(226, 145, 63, .16); }
.sheet a { color: var(--link); font-weight: 700; }

.form-note {
  background: rgba(226, 145, 63, .14);
  border-left: 3px solid var(--ember);
  padding: 10px 12px;
  font-family: var(--font-ui);
  font-size: 12px;
  margin-bottom: 16px;
}

.field { margin-bottom: 12px; font-family: var(--font-ui); font-size: 12px; }
.field label { display: block; margin-bottom: 4px; color: var(--ink-soft); }
.field input, .field select {
  width: 100%;
  padding: 7px 9px;
  border: 1px solid var(--panel-edge);
  background: #fbf7f2;
  font-family: var(--font-ui);
  font-size: 12px;
  color: var(--ink);
  outline: none;
}
.field input:focus { border-color: var(--ember); }

.btn-primary {
  padding: 8px 22px;
  border: 1px solid #a8641f;
  border-radius: 4px;
  background: linear-gradient(#ffc070, #e2913f 55%, #b96f28);
  color: #4a2408;
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 12px;
  cursor: pointer;
}
.btn-primary:hover { filter: brightness(1.08); }

/* pagination */

.pager { text-align: center; padding: 12px 0; font-family: var(--font-ui); }
.pager a {
  display: inline-grid;
  place-items: center;
  width: 29px; height: 29px;
  margin: 0 3px;
  color: #2b3f5c;
  font-size: 12px;
}
.pager a.is-active { background: url("/assets/img/chaos/layout/page_sel.webp") center no-repeat; color: #8a3a20; }

/* ---------------------------------------------------------- class band -- */

/* Sits below the frame, on the page background rather than inside the slab:
   it is a poster, not a panel, and boxing it would make the page read as two
   stacked frames. */
.classes-band {
  /* Same width as the slab above (.bottom-container), so the cards line up
     with the frame instead of running 24px past it on each side. */
  width: calc(100% - 48px);
  margin: 34px auto 6px;
}
@media (max-width: 860px) { .classes-band { width: 100%; } }

.classes-head { text-align: center; margin-bottom: 18px; }
.classes-head h2 {
  margin: 0;
  font-family: var(--font-head);
  font-size: 20px;
  font-weight: 500;
  letter-spacing: .10em;
  text-transform: uppercase;
  color: var(--chrome);
  text-shadow: 0 2px 12px #000, 0 0 22px rgba(226, 145, 63, .25);
}
.classes-head p {
  margin-top: 6px;
  font-family: var(--font-ui);
  font-size: 12px;
  color: var(--chrome-dim);
}

/* ⚠ Five equal tracks, and the images are given an aspect ratio. Without it
   the portraits arrive at their own 900px height and the band becomes five
   full-height pictures stacked down the page. */
.class-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 12px;
}

.class-card {
  position: relative;
  overflow: hidden;
  border: 1px solid rgba(141, 155, 176, .28);
  border-radius: 3px;
  background: var(--navy);
  box-shadow: inset 0 1px 0 rgba(214, 221, 232, .07), 0 8px 24px rgba(0, 0, 0, .45);
  transition: border-color .2s ease, transform .2s ease, box-shadow .2s ease;
}
.class-card:hover {
  border-color: var(--ember);
  transform: translateY(-2px);
  box-shadow: 0 10px 28px rgba(226, 145, 63, .22);
}

.class-card img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  object-position: center 18%;    /* keep faces in frame, not chests */
}

/* The caption sits on the picture, on its own gradient, so no fixed height
   has to be reserved for it. */
.cc-body {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 26px 10px 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: linear-gradient(rgba(7, 13, 21, 0), rgba(7, 13, 21, .88) 55%, rgba(7, 13, 21, .96));
}
.cc-name {
  font-family: var(--font-head);
  font-size: 13px;
  letter-spacing: .04em;
  color: var(--chrome);
  text-shadow: 0 2px 6px #000;
}
.cc-tag {
  font-family: var(--font-ui);
  font-size: 11px;
  color: var(--chrome-dim);
}

@media (max-width: 1120px) {
  .class-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 640px) {
  .class-grid { grid-template-columns: repeat(2, 1fr); gap: 8px; }
  .classes-head h2 { font-size: 17px; }
}

/* --------------------------------------------------------------- footer -- */

/* The closing piece.

   A thin link band, the crystal crown centred beneath it, then a deep field
   that fades out with the mark ghosted into it. The crown uses its own aspect
   ratio, same rule as the menu bar: never stretched, never cropped. */
/* Just the mark and one line. No crown, no backdrop: the page tone carries
   straight through to the bottom edge. */
.footer {
  position: relative;
  margin-top: 0;
  padding: 30px 20px 54px;
  text-align: center;
}





/* The field below: a warm bloom under the crown, fading into the page. */
.footer-deep { position: relative; }

/* A signature, not a poster. */
.footer-mark {
  display: block;
  width: 132px;
  height: 65px;                    /* 132 / 2.037, the wordmark's aspect */
  margin: 0 auto;
  background: url("/assets/img/chaos/layout/logo-murealm.webp") center / contain no-repeat;
  opacity: .42;
  transition: opacity .3s ease;
}
.footer-mark:hover { opacity: .72; }

.footer-legal {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0 9px;
  margin-top: 14px;
  font-family: var(--font-ui);
  font-size: 12px;
}
.footer-legal a { color: var(--chrome-dim); }
.footer-legal a:hover { color: var(--ember-hot); filter: none; }
.footer-legal span { color: var(--rule); }

.footer .copyright {
  margin: 9px auto 0;
  color: #566173;
  font-size: 11px;
  font-family: var(--font-ui);
  letter-spacing: .02em;
  text-align: center;
}

@media (max-width: 860px) {
  .footer { padding: 24px 16px 40px; }
}

/* ------------------------------------------------------------ utilities -- */

.hidden { display: none; }

/* Not built yet, and saying so.

   Every place on the site that would otherwise be a link to nowhere uses this
   instead: the top bar, the sidebar blocks with no data source, the services
   list. It is deliberately not a link - a dead href teaches people that the
   site's links cannot be trusted. */
.is-soon {
  color: #5b6879;
  cursor: default;
  font-style: italic;
}
.top-links .is-soon { padding: 0 14px; border-right: 1px solid var(--rule); }

/* ⚠ In the crown it is NOT italic and NOT smaller. The menu is one row of
   roman capitals; an italic entry in the middle of it looked like a mistake
   rather than like a label. Its metrics live with .menu a, up in the crown
   section, so the two can never drift apart again. */

/* On the parchment column the same idea needs a light-background tone. */
.content .is-soon { color: #8a8078; }

/* The "not yet" state, in the sidebar.

   Same idea as the services page: the block keeps its place and says what it
   is waiting for, so the page reads as the finished shape with holes in it
   rather than as a page that is missing parts. The dashed edge is what tells
   it apart from a block carrying real data at a glance. */
.soon-note {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  font-family: var(--font-ui);
  font-size: 11px;
  line-height: 1.5;
  color: var(--chrome-dim);
  background: rgba(255, 255, 255, .03);
  border: 1px dashed rgba(141, 155, 176, .3);
  border-radius: 3px;
}
.soon-note a { color: var(--chrome-dim); text-decoration: underline; text-underline-offset: 2px; }
.soon-note a:hover { color: var(--ember); filter: none; }

/* The reason runs as a paragraph with the tag leading it, so the block reads
   top to bottom instead of as a label with a line beside it. */
.soon-note.is-wide { display: block; }
.soon-note.is-wide .soon-tag { margin-bottom: 6px; }

/* The link to the full list sits on its own line under the reason. */
.soon-more {
  display: block;
  margin-top: 7px;
  padding-top: 6px;
  border-top: 1px solid rgba(141, 155, 176, .22);
  font-size: 11px;
}

.soon-tag {
  flex: none;
  display: inline-block;
  padding: 1px 7px;
  border: 1px solid rgba(226, 145, 63, .45);
  border-radius: 2px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .10em;
  text-transform: uppercase;
  color: var(--ember);
  background: rgba(226, 145, 63, .10);
}

/* Nothing to override any more: every block title is the same tone. A
   placeholder is told apart by its dashed note, not by a dimmer heading. */

/* ------------------------------------------------ layout variants ------- */

/* ⚠ is-rank gets NO track override. The sidebars must be the same width on
   every page - the rankings page once narrowed them to buy the boards room,
   and the status block visibly changed size between pages. The boards live
   inside minmax(0, 1fr) and scroll within .content if they must. */

/* The character sheet and the guild page bring their own two panes, so they
   run without a left column and the content takes that track. */
.bottom-container.no-left  { grid-template-columns: minmax(0, 1fr) 236px; }
.bottom-container.no-right { grid-template-columns: 236px minmax(0, 1fr); }
.bottom-container.no-left.no-right { grid-template-columns: minmax(0, 1fr); }

.bottom-container.no-left .content  { grid-column: 1; border-left: 0; }
.bottom-container.no-left .right-sidebar { grid-column: 2; }
.bottom-container.no-right .content { border-right: 0; }


/* ========================================================================== */
/* RANKINGS                                                                   */
/* ========================================================================== */

/* the board menu in the sidebar */

.rank-menu { font-family: var(--font-ui); }

.rank-group + .rank-group { margin-top: 12px; }

.rank-group-t {
  padding: 4px 0 5px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--rule);
  font-family: var(--font-head);
  font-size: 11px;
  letter-spacing: .10em;
  text-transform: uppercase;
  color: var(--ember);
}

/* ⚠ Blocks, not inline. These arrive from the API as a flat run of anchors,
   and as inline elements they ran together into one paragraph of board names
   with no way to tell where one ended and the next began. */
.rank-item {
  display: block;
  padding: 4px 8px;
  font-size: 11.5px;
  line-height: 1.35;
  color: var(--chrome-dim);
  border-left: 2px solid transparent;
}
a.rank-item:hover { color: var(--ember-hot); border-left-color: var(--ember); filter: none; }
a.rank-item.active {
  color: var(--chrome);
  border-left-color: var(--ember);
  background: rgba(226, 145, 63, .08);
}

/* A board the game does not track yet: listed, but plainly not a link. */
.rank-item.is-soon { color: #5b6879; cursor: default; }
.rank-item.is-soon:hover { color: #5b6879; border-left-color: transparent; }
.rank-item .soon { font-style: normal; opacity: .55; margin-left: 4px; }

/* the board itself */

.board-tools { display: flex; align-items: center; }

.board-search input {
  width: 190px;
  max-width: 40vw;
  padding: 4px 9px;
  border: 1px solid rgba(141, 155, 176, .5);
  border-radius: 2px;
  background: rgba(255, 255, 255, .55);
  font-family: var(--font-ui);
  font-size: 11.5px;
  color: var(--ink);
  outline: none;
}
.board-search input:focus { border-color: var(--ember); background: #fff; }

.board-note {
  margin-bottom: 14px;
  padding: 9px 12px;
  font-family: var(--font-ui);
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--ink-soft);
  background: rgba(226, 145, 63, .12);
  border-left: 3px solid var(--ember);
}

.board-missing {
  padding: 22px 18px;
  text-align: center;
  font-family: var(--font-ui);
  font-size: 12px;
  color: var(--ink-soft);
  border: 1px dashed var(--panel-edge);
  border-radius: 3px;
}
.board-missing p { margin-top: 8px; line-height: 1.55; }

/* ⚠ The boards are wider than the column. They scroll inside their own box;
   the page itself must never scroll sideways.

   The max-width is not optional: overflow-x on a box that is free to grow
   does nothing at all, and the reset ladder simply ran off the edge of the
   parchment with its last column cut in half. */
.board-scroll {
  overflow-x: auto;
  max-width: 100%;
}
.content-block { min-width: 0; }

.board-table { min-width: 100%; }
.board-table td, .board-table th { white-space: nowrap; }
.board-table .td-name { white-space: normal; }

/* ⚠ WRITTEN AT `.sheet th` WEIGHT, and that is the whole point.

   `.sheet th { text-align: left }` is a class plus an element; a bare
   `.th-num` is one class, so it loses. The headings stayed left while the
   numbers under them went right, and every numeric column read as if its
   values had slid out from under their own title. Any new column type has to
   be added here at this weight, not as a bare class. */
.sheet th.th-rank, .sheet td.td-rank,
table.board-table th.th-rank, table.board-table td.td-rank {
  width: 34px;
  text-align: center;
}

.sheet th.th-num,     .sheet td.td-num,
.sheet th.th-opt,     .sheet td.td-opt,
.sheet th.th-money,   .sheet td.td-money,
.sheet th.th-prestige, .sheet td.td-prestige,
.sheet th.th-mile,    .sheet td.td-mile {
  text-align: right;
}
/* The milestone column is a single star, centred under its own heading. */
.sheet th.th-mile, .sheet td.td-mile { text-align: center; }
/* "opt" columns (a number that may be absent, see rankings.js) come in
   pairs that belong together - the quest a character is on and the kills
   made on it - so they sit a little closer than the other numbers. */
.sheet th.th-opt, .sheet td.td-opt { padding-left: 8px; }

.sheet th.th-online, .sheet td.td-online { width: 22px; text-align: center; }

/* The name cell carries the guild crest and the character link.

   ⚠ The crest's SPACE is reserved even when there is no crest to draw. The
   emblem is only rendered for a character who is in a guild - an empty box
   read as a guild with a blank mark - but if the space went with it, every
   name without a guild started 23px to the left and the whole column looked
   broken. The crest is taken out of the flow and the names stay on one line. */
/* ⚠ `.sheet td` sets the padding and is one specificity step above a bare
   `.td-name`, so the reserved space has to be written at the same weight or
   higher - otherwise the rule is ignored, the padding stays at 12px and the
   name sits ON TOP of the crest. That is exactly what happened.

   12px cell padding + 16px crest + 7px gap = 35px. */
.sheet td.td-name,
table.board-table td.td-name {
  position: relative;
  padding-left: 35px;
}
.td-name .gmark-link {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
}
.bt-name { font-weight: 700; }

/* The guild board puts the emblem beside the guild's own name, where every
   row has one, so it stays in the flow there. */
.td-guild_name { padding-left: 0; }

.rk {
  display: inline-grid;
  place-items: center;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: 2px;
  background: rgba(0, 0, 0, .07);
  font-size: 11px;
  color: var(--ink-soft);
}

/* The top three, in the metals everybody already reads. */
.medal {
  display: inline-grid;
  place-items: center;
  width: 19px;
  height: 19px;
  border-radius: 50%;
  font-size: 11px;
  font-weight: 700;
  color: #2b2118;
  box-shadow: inset 0 -2px 3px rgba(0, 0, 0, .25);
}
.medal.g { background: linear-gradient(#ffe08a, #e0a534); }
.medal.s { background: linear-gradient(#eef2f7, #b3bdcb); }
.medal.b { background: linear-gradient(#e8b98c, #b9743c); }

/* The online lamp.

   Same two pieces the server-status block uses, rather than a CSS dot: they
   are lit gems with their own glow baked in, and a flat circle beside them
   looked like a different site's widget. 11px so the art keeps its shape. */
.gem {
  display: inline-block;
  width: 11px;
  height: 11px;
  background: url("/assets/img/chaos/layout/off-status.webp") center / contain no-repeat;
}
.gem.gem-on { background-image: url("/assets/img/chaos/layout/on-status.webp"); }

.gmark-link { display: inline-flex; align-items: center; gap: 6px; }
.gmark-link.inline .gn { font-weight: 700; color: var(--link); }

/* ⚠ In the parchment column the emblem sits on a light background, so it
   needs the dark outline the sidebar version gets from its own backdrop. */
.content .gmark { outline-color: rgba(0, 0, 0, .35); }
.content .gmark-none { background: rgba(0, 0, 0, .07); }

.bt-empty { text-align: center; color: var(--ink-soft); font-style: italic; padding: 22px 0; }

.board-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 12px;
}
.board-total { font-family: var(--font-ui); font-size: 11.5px; color: var(--ink-soft); }

/* pager buttons drawn by rankings.js */
.pager .pg {
  display: inline-grid;
  place-items: center;
  min-width: 27px;
  height: 27px;
  margin: 0 2px;
  padding: 0 6px;
  cursor: pointer;
  border-radius: 2px;
  font-family: var(--font-ui);
  font-size: 12px;
  color: #2b3f5c;
}
.pager .pg:hover { background: rgba(226, 145, 63, .18); }
.pager .pg.active {
  background: url("/assets/img/chaos/layout/page_sel.webp") center no-repeat;
  color: #8a3a20;
  font-weight: 700;
}
.pager .pg-gap { color: var(--ink-soft); padding: 0 3px; }

/* the floating panel: guild names, and the prestige ledger */

.board-tip {
  position: fixed;
  z-index: 40;
  max-width: 320px;
  padding: 8px 10px;
  pointer-events: none;
  font-family: var(--font-ui);
  font-size: 11.5px;
  color: var(--chrome);
  background: rgba(8, 15, 25, .97);
  border: 1px solid rgba(226, 145, 63, .35);
  border-radius: 3px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .6);
}
.board-tip.is-wide { max-width: 380px; }
.board-tip[hidden] { display: none; }

.tip-plain { color: var(--chrome); }

.parts-head, .parts-line, .parts-total {
  display: grid;
  grid-template-columns: 1fr 44px 58px;
  gap: 6px;
  padding: 3px 0;
}
.parts-head {
  color: var(--chrome-dim);
  font-size: 11px;
  letter-spacing: .06em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--rule);
}
.parts-line { color: var(--chrome-dim); }
.parts-line span:nth-child(2), .parts-total span:nth-child(2) { text-align: right; }
.pp { text-align: right; color: var(--ember); }
.parts-total {
  border-top: 1px solid var(--rule);
  margin-top: 3px;
  color: var(--chrome);
}

.row-lit td { background: rgba(226, 145, 63, .14) !important; }

/* ---------------------------------------------------------- responsive -- */

/* Columns narrow first, then fold under the content, then everything stacks.
   The logo block keeps its 338px height at every step so the menu never jumps. */

@media (max-width: 1180px) {
  /* The frame is fluid here (100% - 40px), so a fixed 96px bleed would poke
     past the viewport at every width - 16px keeps the drawn overhang and
     stays inside the 40px the frame leaves. Still no vw units. */
  .menu { width: calc(100% + 16px); margin-left: -8px; }
  .bottom-container { grid-template-columns: 210px minmax(0, 1fr) 210px; }
  /* ⚠ Only the SIDES narrow. The 56px top is what keeps the first heading
     below the crown - at 26px it sat half-hidden under the bar art here. */
  .content { padding: 56px 26px 40px; }
  /* The side padding is 26px here: a 30px pull would hang the tab over the
     column border. 22px keeps it inside the sheet at every step. */
}

@media (max-width: 1120px) {
  /* right column drops under the content, left stays beside it */
  .bottom-container { grid-template-columns: 210px minmax(0, 1fr); }
  /* ⚠ Repeated on purpose: .no-left is two classes deep and outranks the rule
     above, so /character and /guild kept a 236px right rail here while every
     other page had already folded it under - the same block was two different
     sizes depending on the page. */
  .bottom-container.no-left { grid-template-columns: minmax(0, 1fr); }
  .bottom-container.no-left .right-sidebar { grid-column: 1 / -1; }
  .right-sidebar {
    grid-column: 1 / -1;
    grid-row: 2;
    border-top: 1px solid rgba(141, 155, 176, .22);
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 0 26px;
    align-items: start;
    /* Folded under the content there is no crown above this column - the
       88px clearance belongs only to a column that starts under the bar. */
    padding-top: 26px;
  }
}

@media (max-width: 860px) {
  .wrapper, .top-panel-inner { width: min(100% - 24px, 720px); }

  .logo { padding-top: 58px; }
  .logo a { width: 216px; height: 161px; }

  /* ⚠ THE BAR STOPS BEING A BAR HERE.

     The crown is one 6.84:1 image with the links laid inside it. Below this
     width it is under 105px tall, and eight links cannot sit on one line in
     it: they wrapped straight out of the artwork and landed on the panel
     underneath. So the picture is dropped and the menu becomes what it
     actually is at this size - a block of links on its own dark plate, which
     wraps as many rows as it needs.

     Everything that made it overhang the frame is unwound with it: the width,
     the negative side margin, the aspect ratio and the overlap below. */
  .menu {
    width: 100%;
    margin: 18px 0 0;
    padding: 8px 10px;
    aspect-ratio: auto;
    background: linear-gradient(rgba(14, 26, 39, .95), rgba(7, 13, 21, .96));
    border: 1px solid rgba(141, 155, 176, .3);
    border-radius: 4px;
  }
  .menu::before { display: none; }
  .menu ul { flex-wrap: wrap; gap: 2px 4px; }
  /* No bar art at this width, so there are no stones for the light to sit on. */
  .menu .mgem { display: none; }
  .menu a, .menu .is-soon { font-size: 12px; padding: 7px 10px 8px; }

  /* No bar to slide under, so nothing to pull the panel up into. */
  .bottom-container { margin-top: 12px; }

  /* ⚠ The variant selectors have to be repeated here.
     .bottom-container.is-rank / .no-left / .no-right are two classes deep, so
     they outrank a single-class rule in this media query no matter which
     comes last in the file - and their 236px side track kept a column beside
     the content at phone width, which is where the 15px of sideways scroll on
     /character, /guild and 404 came from. */
  .bottom-container,
  .bottom-container.is-rank,
  .bottom-container.no-left,
  .bottom-container.no-right,
  .bottom-container.no-left.no-right { grid-template-columns: minmax(0, 1fr); width: 100%; }

  .bottom-container.no-left .right-sidebar { grid-column: 1; }
  .left-sidebar  { grid-column: 1; grid-row: 2; }
  .bottom-container::before, .bottom-container::after { display: none; }
  .content       { grid-column: 1; grid-row: 1; padding: 24px 20px 32px; border-left: 0; border-right: 0; border-top: 1px solid rgba(141,155,176,.3); border-bottom: 1px solid rgba(141,155,176,.3); }
  /* Side padding is 20px here - see the 1180px note on the arrow pull. */
  .right-sidebar { grid-column: 1; grid-row: 3; }

  .left-sidebar,
  .right-sidebar {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 0 24px;
    /* Everything is stacked and the crown is gone - same 24px top as the
       content, not the under-the-bar clearance. */
    padding-top: 24px;
  }

  .content-title { padding: 0 30px; font-size: 13px; }
  .top-links a { padding: 0 9px; font-size: 11px; }
}

@media (max-width: 560px) {
  .logo { padding-top: 46px; }
  .logo a { width: 190px; height: 142px; }
  .menu { margin-top: 14px; }
  .menu a, .menu .is-soon { font-size: 11.5px; padding: 6px 8px 7px; }
  .top-social { display: none; }
  .gallery li { flex: 0 0 calc((100% - 10px) / 2); }
  .news-head { flex-direction: column; gap: 2px; }
}

/* ========================================================================== */
/* CONTENT: the shared vocabulary of the parchment column                     */
/* ========================================================================== */

.content h3 {
  margin: 22px 0 8px;
  font-family: var(--font-head);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: .04em;
  color: #2a2620;
}
.content-block > h3:first-child { margin-top: 0; }

.content p { line-height: 1.6; }
.content p + p { margin-top: 10px; }

.lead {
  font-family: var(--font-ui);
  font-size: 13px;
  line-height: 1.65;
  color: #2a2620;
  margin-bottom: 14px;
}

.hint {
  font-family: var(--font-ui);
  font-size: 11px;
  line-height: 1.5;
  color: var(--ink-soft);
  margin-top: 4px;
}

.empty-note { font-style: italic; color: var(--ink-soft); }

/* The one warning on the site that is not decoration: passwords are stored in
   clear text by this server engine, so nobody must ever be talked out of one. */
.warn-note {
  margin-top: 18px;
  padding: 10px 12px;
  font-family: var(--font-ui);
  font-size: 11.5px;
  line-height: 1.55;
  color: #6a2f1c;
  background: rgba(192, 57, 43, .10);
  border-left: 3px solid #c0392b;
}

/* Shown in place of anything that needs a signed-in account. */
.guard {
  padding: 26px 18px;
  text-align: center;
  font-family: var(--font-ui);
  font-size: 12.5px;
  color: var(--ink-soft);
  border: 1px dashed var(--panel-edge);
  border-radius: 3px;
}

.form-msg {
  margin: 10px 0;
  padding: 9px 11px;
  font-family: var(--font-ui);
  font-size: 11.5px;
  line-height: 1.5;
  border-radius: 2px;
}
.form-msg.is-good { color: #1f5c2a; background: rgba(111, 174, 58, .18); border-left: 3px solid var(--online); }
.form-msg.is-bad  { color: #7a2018; background: rgba(192, 57, 43, .14);  border-left: 3px solid var(--offline); }

.cta-row { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 20px; }

.btn-ghost {
  display: inline-block;
  padding: 8px 22px;
  border: 1px solid #a9a094;
  border-radius: 4px;
  background: rgba(255, 255, 255, .4);
  color: #3a3128;
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 12px;
  cursor: pointer;
}
.btn-ghost:hover { border-color: var(--ember); color: #7a3d10; filter: none; }

a.btn-primary { display: inline-block; text-decoration: none; }
a.btn-primary:hover { filter: brightness(1.08); }

/* facts: the strip on About, the list on the account pages */

/* THE FACT STRIP: stone, not paper.

   These were pale boxes on a pale sheet - the same rectangle four times over,
   which is exactly what makes a page read as a grid of cubes. They are cut
   from the same dark material as the frame instead, so the light column
   carries a piece of the plate through it, and the corner is chamfered on the
   diagonal the rest of the design uses. */
.fact-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(118px, 1fr));
  gap: 8px;
  margin: 16px 0 20px;
}
.fact {
  position: relative;
  padding: 11px 12px 12px;
  font-family: var(--font-ui);
  text-align: center;
  color: var(--chrome-dim);
  background: linear-gradient(rgba(22, 40, 58, .96), rgba(10, 18, 28, .97));
  border: 1px solid rgba(141, 155, 176, .30);
  box-shadow:
    inset 0 1px 0 rgba(214, 221, 232, .10),
    0 4px 14px rgba(0, 0, 0, .22);
  /* One 10px chamfer, top-left and bottom-right - the same cut every panel on
     the site takes. */
  clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
}
.fact b {
  display: block;
  font-family: var(--font-head);
  font-size: 13px;
  line-height: 1.25;
  color: var(--chrome);
  text-shadow: 0 1px 0 #000;
}
.fact span {
  display: block;
  margin-top: 3px;
  font-size: 11px;
  letter-spacing: .10em;
  text-transform: uppercase;
  color: #7d8ca3;
}
/* The first one is the headline fact and gets the ember. */
.fact-badge {
  border-color: rgba(226, 145, 63, .55);
  background: linear-gradient(rgba(58, 36, 16, .96), rgba(20, 14, 9, .97));
}
.fact-badge b { color: var(--ember-hot); text-shadow: 0 0 14px rgba(226, 145, 63, .4); }

.fact-list { font-family: var(--font-ui); font-size: 12px; }
.fact-list > div {
  padding: 9px 0;
  border-bottom: 1px solid var(--panel-edge);
}
.fact-list > div:last-child { border-bottom: 0; }
.fact-list b { display: block; color: #2a2620; margin-bottom: 2px; }
.fact-list span { color: var(--ink-soft); line-height: 1.5; }

/* picture rows */

.shots { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 16px 0; }
.shots-one { grid-template-columns: 1fr; }
.shots figure { margin: 0; }
.shots img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border: 1px solid var(--panel-edge);
  border-radius: 2px;
  box-shadow: 0 3px 12px rgba(0, 0, 0, .18);
}

/* ========================================================================== */
/* REGISTRATION                                                               */
/* ========================================================================== */

.reg-captcha { display: flex; align-items: center; gap: 8px; }

/* The captcha is an inline SVG from /api/captcha, sized here so a wider or
   narrower drawing cannot move the field beside it. */
.cap-img {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 132px;
  height: 40px;
  border: 1px solid var(--panel-edge);
  border-radius: 2px;
  background: #fbf7f2;
  overflow: hidden;
}
.cap-img svg, .cap-img img { max-width: 100%; height: 100%; }
.cap-input { width: 110px !important; }
.cap-new {
  width: 34px;
  height: 34px;
  cursor: pointer;
  border: 1px solid var(--panel-edge);
  border-radius: 2px;
  background: rgba(255, 255, 255, .5);
  font-size: 15px;
  color: var(--ink-soft);
}
.cap-new:hover { border-color: var(--ember); color: #7a3d10; }

.reg-agree { margin: 14px 0; font-family: var(--font-ui); font-size: 12px; }
.reg-check { display: flex; align-items: flex-start; gap: 7px; margin-bottom: 6px; cursor: pointer; }
.reg-check input { margin-top: 2px; }

.reg-done { text-align: center; padding: 26px 18px; font-family: var(--font-ui); }
.rd-ico {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  margin: 0 auto 10px;
  border-radius: 50%;
  background: rgba(111, 174, 58, .18);
  border: 1px solid var(--online);
  color: #2b6b1e;
  font-size: 20px;
}
.reg-done b { display: block; font-size: 14px; margin-bottom: 6px; }
.reg-done p { margin-bottom: 14px; color: var(--ink-soft); }

/* ========================================================================== */
/* SERVICES                                                                   */
/* ========================================================================== */

.sv-group { margin-bottom: 20px; }
.sv-group-t {
  margin: 0 0 8px;
  padding-bottom: 5px;
  border-bottom: 1px solid var(--panel-edge);
  font-family: var(--font-head);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .05em;
  color: #2a2620;
}

.sv-group-t { text-align: center; }

/* One centred link per service, the reference server's list. */
.sv-links { list-style: none; margin: 0; padding: 0; }
.sv-links li {
  border-bottom: 1px solid rgba(206, 193, 178, .7);
  text-align: center;
}
.sv-links li:last-child { border-bottom: 0; }
.sv-links a,
.sv-links .sv-link {
  display: block;
  width: 100%;
  padding: 6px 4px;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: var(--font-ui);
  font-size: 12px;
  color: var(--link);
  text-decoration: none;
}
.sv-links a::before,
.sv-links .sv-link::before { content: "\00bb\00bb\00bb"; float: left; color: var(--ink-soft); }
.sv-links a::after,
.sv-links .sv-link::after  { content: "\00ab\00ab\00ab"; float: right; color: var(--ink-soft); }
.sv-links a:hover,
.sv-links .sv-link:hover { background: rgba(226, 145, 63, .10); text-decoration: underline; }

.cr-caret { flex: none; color: var(--ink-soft); transition: transform .18s ease; }
.is-open .cr-caret { transform: rotate(90deg); }


.cc-act {
  display: inline-block;
  padding: 5px 14px;
  cursor: pointer;
  border: 1px solid #a8641f;
  border-radius: 3px;
  background: linear-gradient(#ffc070, #e2913f 55%, #b96f28);
  color: #4a2408;
  font-family: var(--font-ui);
  font-size: 11.5px;
  font-weight: 700;
}
.cc-act:hover { filter: brightness(1.08); }
.cc-act.is-soon, .cc-act:disabled {
  border-color: #b9b0a4;
  background: rgba(0, 0, 0, .05);
  color: #8a8078;
  cursor: not-allowed;
  filter: none;
}

/* ========================================================================== */
/* RESETS                                                                     */
/* ========================================================================== */

.rs-views { display: flex; gap: 4px; }
.rs-view {
  padding: 3px 10px;
  cursor: pointer;
  border: 1px solid var(--panel-edge);
  border-radius: 2px;
  background: rgba(255, 255, 255, .45);
  font-family: var(--font-ui);
  font-size: 11px;
  color: var(--ink-soft);
}
.rs-view.is-on { border-color: var(--ember); background: rgba(226, 145, 63, .16); color: #7a3d10; }

/* ⚠ The ladder's numbers must not wrap. "1 000" broken across two lines
   reads as two different figures, and the band columns ("5 kk-45 kk") are
   worse: half a range on each line. The table scrolls in its own box on a
   narrow window instead. */
.rs-table td, .rs-table th { white-space: nowrap; }
.rs-bonus { color: #7a3d10; font-weight: 700; white-space: nowrap; }
.rs-boost { color: #1b4b7a; }
.bt-none { color: #a49a8e; }

/* Rows past the unlocked cap. Dimmed rather than hidden: the whole ladder is
   what a player is deciding against. */
.rs-locked td { opacity: .45; }
.rs-milestone td { background: rgba(226, 145, 63, .10); }

.rs-legend {
  margin-top: 10px;
  font-family: var(--font-ui);
  font-size: 11px;
  line-height: 1.5;
  color: var(--ink-soft);
}

/* one card per character, opening on click */

.rs-char { border-bottom: 1px solid rgba(206, 193, 178, .7); }
.rs-char:last-child { border-bottom: 0; }

.rs-char-main {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 4px;
  font-family: var(--font-ui);
  font-size: 12px;
}
.rs-char-name { font-weight: 700; }
.rs-char-class, .rs-char-lvl, .rs-char-res, .rs-char-zen { color: var(--ink-soft); font-size: 11.5px; }
.rs-char-no { color: #8a8078; font-size: 11px; font-style: italic; }

.rs-go, .rs-do {
  margin-left: auto;
  padding: 5px 14px;
  cursor: pointer;
  border: 1px solid #a8641f;
  border-radius: 3px;
  background: linear-gradient(#ffc070, #e2913f 55%, #b96f28);
  color: #4a2408;
  font-family: var(--font-ui);
  font-size: 11.5px;
  font-weight: 700;
}
.rs-go:disabled, .rs-do:disabled {
  border-color: #b9b0a4;
  background: rgba(0, 0, 0, .05);
  color: #8a8078;
  cursor: not-allowed;
}

.rs-panel {
  padding: 10px 4px 14px 4px;
  font-family: var(--font-ui);
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--ink-soft);
}
.rs-sum { display: flex; flex-wrap: wrap; gap: 8px 18px; margin-bottom: 8px; }
.rs-sum b { color: var(--ink); }
.rs-warn, .rs-bad { color: #7a2018; }
.rs-wait { color: #7a3d10; }
.rs-msg { margin-top: 8px; padding: 8px 10px; border-radius: 2px; }
.rs-msg.is-good { color: #1f5c2a; background: rgba(111, 174, 58, .18); }
.rs-msg.is-bad  { color: #7a2018; background: rgba(192, 57, 43, .14); }

.rs-bar {
  height: 6px;
  margin: 4px 0 2px;
  border-radius: 3px;
  background: rgba(0, 0, 0, .10);
  overflow: hidden;
}
.rs-bar i { display: block; height: 100%; background: linear-gradient(90deg, #e2913f, #ffc070); }

/* the gate list in the sidebar uses .rs-gates, defined with the dark blocks */
.rs-gate-top { display: flex; justify-content: space-between; gap: 6px; }
.rs-gate-note { color: var(--chrome-dim); font-size: 11px; }

/* ========================================================================== */
/* ACCOUNT: the character list                                                */
/* ========================================================================== */

.char-row { border-bottom: 1px solid rgba(206, 193, 178, .7); }
.char-row:last-child { border-bottom: 0; }

.cr-head {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 4px;
  cursor: pointer;
  border: 0;
  background: none;
  text-align: left;
  font-family: var(--font-ui);
  font-size: 12px;
  color: var(--ink);
}
.cr-head:hover { background: rgba(226, 145, 63, .10); }

/* Every character row leads with its class art - the same keyart the class
   band uses - so the list reads as a hall of characters, not as a table.
   Summoner and Rage Fighter have no card art yet and get the plain plate. */
.cr-head::before {
  content: "";
  flex: 0 0 46px;
  height: 46px;
  border-radius: 3px;
  background:
    var(--cls-art, linear-gradient(rgba(38, 52, 74, .9), rgba(18, 27, 42, .9)))
    center 18% / cover no-repeat;
  box-shadow: inset 0 0 0 1px rgba(141, 155, 176, .4), 0 2px 6px rgba(0, 0, 0, .35);
}
.cls-tint-0 .cr-head::before { --cls-art: url("/assets/img/gen/keyart/class-dw.webp"); }
.cls-tint-1 .cr-head::before { --cls-art: url("/assets/img/gen/keyart/class-dk.webp"); }
.cls-tint-2 .cr-head::before { --cls-art: url("/assets/img/gen/keyart/class-elf.webp"); }
.cls-tint-3 .cr-head::before { --cls-art: url("/assets/img/gen/keyart/class-mg.webp"); }
.cls-tint-4 .cr-head::before { --cls-art: url("/assets/img/gen/keyart/class-dl.webp"); }

.cr-name { font-weight: 700; }
.cr-class, .cr-lvl, .cr-res { color: var(--ink-soft); font-size: 11.5px; }
.cr-res { margin-left: auto; }
.cr-body { padding: 4px 4px 14px 24px; }

.cc-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 4px 16px;
  margin: 0 0 12px;
  font-family: var(--font-ui);
  font-size: 11.5px;
}
.cc-facts > div { display: flex; justify-content: space-between; gap: 8px; padding: 3px 0; border-bottom: 1px solid rgba(206, 193, 178, .5); }
.cc-facts dt { color: var(--ink-soft); }
.cc-facts dd { margin: 0; color: var(--ink); }
.cc-guild { font-weight: 700; }

.cc-acts { display: flex; flex-wrap: wrap; gap: 6px; }
.cc-view { background: rgba(255, 255, 255, .5); border-color: #a9a094; color: #3a3128; }

.cc-msg { margin-top: 8px; padding: 8px 10px; border-radius: 2px; font-family: var(--font-ui); font-size: 11.5px; }
.cc-msg.is-good { color: #1f5c2a; background: rgba(111, 174, 58, .18); }
.cc-msg.is-bad  { color: #7a2018; background: rgba(192, 57, 43, .14); }

/* the daily gift */

.gf-lead { font-family: var(--font-ui); font-size: 12px; margin-bottom: 10px; }
/* One reward per line with its chance on the right. As a plain list the
   label and the percentage ran together into "Jewel of Bless30%". */
.gf-odds { font-family: var(--font-ui); font-size: 11.5px; margin-bottom: 12px; }
.gf-odds li {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 4px 0;
  border-bottom: 1px solid rgba(206, 193, 178, .6);
  color: var(--ink);
}
.gf-odds li:last-child { border-bottom: 0; }
.gf-odds em { font-style: normal; color: var(--ink-soft); }
.gf-last { font-family: var(--font-ui); font-size: 11.5px; color: var(--ink-soft); margin-top: 8px; }
.gf-wait { color: #7a3d10; }
.gf-msg { margin-top: 10px; padding: 9px 11px; border-radius: 2px; font-family: var(--font-ui); font-size: 11.5px; }
.gf-msg.is-good { color: #1f5c2a; background: rgba(111, 174, 58, .18); }
.gf-msg.is-bad  { color: #7a2018; background: rgba(192, 57, 43, .14); }

/* ========================================================================== */
/* ITEMS: the equipment grid, the bag grid and the item panel                 */
/* ========================================================================== */

/* One cell size for every grid on the site, so the vault, the inventory and
   the equipment pane all draw items at the same scale. */
:root { --cell: 34px; }

/* THE EQUIPMENT PANE, laid out like the character window.

   Three columns of worn gear with the jewellery down a narrow fourth, which
   is where the game puts it. The areas are named so character.js only has to
   say which slot a box is - it carries no idea of position at all, and moving
   a slot is one edit here.

       pet     helm    wings   | pendant
       weapon  armor   shield  | ring
       gloves  pants   boots   | ring

   ⚠ Every cell is the same height (grid-auto-rows), so an empty slot leaves
   a hole exactly the size of a full one and the grid never reflows as items
   load in. That was the misalignment: boxes sized by their contents made
   every row a different height. */
.equip-grid {
  /* The game's own equipment window, as the reference site draws it:
       pet     .        helm    .     wings
       weapon  pendant  armor   .     shield
       gloves  ring     pants   ring  boots
     Weapon, armour and shield get the tall middle row; the jewellery sits in
     small boxes at the top of the narrow tracks. */
  display: grid;
  grid-template-columns: minmax(0, 1fr) 40px minmax(0, 1.3fr) 40px minmax(0, 1.2fr);
  grid-template-rows: 92px 178px 100px;
  grid-template-areas:
    "a1 .  b1 .  c1"
    "a2 s1 b2 .  c2"
    "a3 s2 b3 s3 c3";
  gap: 8px;
  padding: 10px;
  border: 1px solid var(--panel-edge);
  background: rgba(255, 255, 255, .25);
}

.eq-a1 { grid-area: a1; } .eq-b1 { grid-area: b1; } .eq-c1 { grid-area: c1; }
.eq-a2 { grid-area: a2; } .eq-b2 { grid-area: b2; } .eq-c2 { grid-area: c2; }
.eq-a3 { grid-area: a3; } .eq-b3 { grid-area: b3; } .eq-c3 { grid-area: c3; }
.eq-s1 { grid-area: s1; } .eq-s2 { grid-area: s2; } .eq-s3 { grid-area: s3; }

.eq-box {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  min-height: 0;
  padding: 6px;
  border: 1px solid #cfc6b8;
  background: #ece6dc;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .6);
}
/* Pendant and rings: a small square at the top of their track. */
.eq-small { align-self: start; height: 40px; padding: 3px; }

/* The item's own picture, as large as the box allows and never stretched. */
.eq-box img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  filter: drop-shadow(0 2px 2px rgba(0, 0, 0, .25));
}
.eq-box[data-item] { cursor: help; }
.eq-box[data-item]:hover { border-color: #b98a4e; }
.eq-empty { background: rgba(236, 230, 220, .55); }

/* ⚠ The bag is one grid with the empty cells drawn first and the items placed
   over them by grid-column/grid-row. The cells and the items therefore have
   to share the same track sizes, which is why the row size is set inline by
   the script and the column size lives here. */
/* The item window, drawn the way the game draws it: a dark panel with a
   thin steel grid, standing on the parchment like a client window. The old
   light cells read as a spreadsheet, not as a warehouse. */
/* The item grid, the way MU4Builder draws it: a near-black well with hair
   thin cell lines, the sprites dropped straight onto it with a shadow, and
   a soft tinted halo UNDER an excellent or ancient piece instead of a box
   around it. No stone textures, no client window art - both were tried and
   read as pasted-in. This is the quiet version, in the site's own steel. */
.bag-grid {
  display: grid;
  grid-template-columns: repeat(8, var(--cell));
  grid-auto-rows: var(--cell);
  gap: 0;
  justify-content: start;
  padding: 6px;
  background: linear-gradient(rgba(9, 14, 22, .98), rgba(6, 10, 17, .98));
  border: 1px solid rgba(141, 155, 176, .35);
  border-radius: 3px;
  box-shadow:
    inset 0 0 26px rgba(0, 0, 0, .9),
    0 6px 18px rgba(0, 0, 0, .4);
  width: max-content;
  max-width: 100%;
  overflow-x: auto;
}
/* Right and bottom hairlines on every cell draw the whole lattice. */
.bag-cell {
  box-shadow:
    inset -1px 0 0 rgba(90, 110, 140, .16),
    inset 0 -1px 0 rgba(90, 110, 140, .16);
}
.bag-item {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: help;
  transition: background-color .15s ease, box-shadow .15s ease;
}
.bag-item:hover {
  background-color: rgba(226, 171, 63, .10);
  box-shadow: inset 0 0 0 1px rgba(226, 171, 63, .55);
  z-index: 1;
}
/* A breath of space around every sprite, and the game's own drop shadow. */
.bag-item img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  padding: 2px;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .8));
}

/* The tier is a glow UNDER the piece, not a frame around it. */
.bag-grid .grade-rare,
.bag-grid .grade-excellent,
.bag-grid .grade-ancient { box-shadow: none; }
.bag-grid .grade-rare      { background-image: radial-gradient(ellipse at center, rgba(90, 150, 215, .20), transparent 72%); }
.bag-grid .grade-excellent { background-image: radial-gradient(ellipse at center, rgba(80, 200, 120, .22), transparent 72%); }
.bag-grid .grade-ancient   { background-image: radial-gradient(ellipse at center, rgba(230, 160, 70, .22), transparent 72%); }
/* An item is ONE cell of its own size, as the game and the reference page
   draw it: the lattice stops at its edge instead of running through the
   piece, and the edge itself is the same hairline as every cell. */
.bag-grid .bag-item {
  box-sizing: border-box;
  background-color: #080d15;
  border-right: 1px solid rgba(90, 110, 140, .16);
  border-bottom: 1px solid rgba(90, 110, 140, .16);
}
.bag-grid .bag-item:hover {
  background-color: #1a1a16;
  box-shadow: inset 0 0 0 1px rgba(226, 171, 63, .55);
}

/* The vault window is sized to its own eight columns and stands centred on
   the parchment like the game's window - stretched to the column width it
   read as a broken dark hole beside the items.
   ⚠ The cells stay at the game's own compact scale (the shared --cell,
   same as the equipment sheet): at 48px the window grew half again taller
   than the screen and read as broken. 8 x 15 small cells IS the game's
   warehouse shape. */
.vt-grid { width: max-content; margin: 0 auto; }

/* The bank pager: one numbered button per bank, the active one lit gold.
   Banks past the account's own count stay visible but locked - the button's
   title says what unlocks them, so a free account can SEE what VIP gives. */
.vt-tabs {
  display: flex;
  gap: 6px;
  margin-bottom: 10px;
  justify-content: center;
}
.vt-tab {
  min-width: 34px;
  height: 30px;
  padding: 0 10px;
  font-family: var(--font-head);
  font-size: 14px;
  color: var(--chrome-dim);
  background: linear-gradient(rgba(38, 52, 74, .95), rgba(18, 27, 42, .95));
  border: 1px solid rgba(141, 155, 176, .4);
  border-radius: 3px;
  cursor: pointer;
  transition: color .15s ease, border-color .15s ease, filter .15s ease;
}
.vt-tab:hover:not(.is-locked) { filter: brightness(1.2); }
.vt-tab.is-active {
  color: var(--ember-hot);
  border-color: rgba(226, 145, 63, .75);
  box-shadow: inset 0 0 6px rgba(226, 145, 63, .25);
}
.vt-tab.is-locked {
  position: relative;
  color: #45506a;
  border-color: rgba(141, 155, 176, .2);
  background: linear-gradient(rgba(22, 30, 44, .95), rgba(12, 18, 29, .95));
  cursor: not-allowed;
}
/* The lock speaks on hover - our own bubble, instant, in the site's cloth;
   the native title tooltip was too slow and skipped some browsers. */
.vt-tab.is-locked:hover::after {
  content: attr(data-lock);
  position: absolute;
  left: 50%;
  bottom: calc(100% + 9px);
  transform: translateX(-50%);
  white-space: nowrap;
  padding: 5px 10px;
  font-family: var(--font-ui);
  font-size: 11px;
  letter-spacing: .02em;
  color: var(--ember-hot);
  background: rgba(6, 11, 19, .97);
  border: 1px solid rgba(226, 145, 63, .5);
  border-radius: 3px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, .6);
  pointer-events: none;
  z-index: 30;
}
.vt-tab.is-locked:hover::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: calc(100% + 4px);
  transform: translateX(-50%) rotate(45deg);
  width: 8px;
  height: 8px;
  background: rgba(6, 11, 19, .97);
  border-right: 1px solid rgba(226, 145, 63, .5);
  border-bottom: 1px solid rgba(226, 145, 63, .5);
  pointer-events: none;
  z-index: 31;
}

/* Item tiers, the colours players already read. Used on the grids and in the
   panel's title line.
   ⚠ NOT inside the game-art bag: the client draws no coloured rings around
   stored items, and the blue boxes on every wing were exactly the
   "kep-lep" that gave the window away as a webpage. In the bag the tier
   speaks through the tooltip; the ring survives on the equipment sheet. */
.grade-normal    { }
.grade-rare      { box-shadow: inset 0 0 0 1px rgba(90, 150, 215, .8); }
.grade-excellent {
  box-shadow:
    inset 0 0 0 1px rgba(80, 200, 120, .85),
    0 0 8px rgba(80, 200, 120, .25);
}
.grade-ancient {
  box-shadow:
    inset 0 0 0 1px rgba(230, 160, 70, .85),
    0 0 8px rgba(230, 160, 70, .25);
}
b.grade-rare      { color: #1b4b7a; }
b.grade-excellent { color: #2d8a4a; }
b.grade-ancient   { color: #a05a12; }

/* The floating item panel: the game's own tooltip, in the game's own voice -
   centred lines, name first in the grade's colour, options in theirs. */
.item-tip {
  position: fixed;
  z-index: 40;
  width: max-content;
  min-width: 230px;
  max-width: 320px;
  padding: 12px 14px;
  pointer-events: none;
  font-family: var(--font-ui);
  font-size: 11px;
  line-height: 1.5;
  text-align: center;
  color: var(--chrome);
  background: rgba(30, 20, 10, .95);
  border: 1px solid rgba(160, 128, 82, .55);
  border-radius: 3px;
  box-shadow:
    inset 0 1px 0 rgba(214, 221, 232, .08),
    0 10px 30px rgba(0, 0, 0, .7);
}
.item-tip[hidden] { display: none; }

.til { display: block; color: #e9e3d8; }
.til-gap { height: 8px; }
.til-dmg    { color: #74a2e8; }
.til-def    { color: #e4d5c3; }
.til-cls    { color: #e9e3d8; }
.til-harm   { color: #ffcc1a; }
.til-opt    { color: #8fb4ef; }
.til-exc    { color: #4fd36a; }
.til-set    { color: #ffcc1a; }
.til-setopt { color: #858585; }
.til-serial { color: #b8b0a3; }

/* The item card, after the reference site: name in the grade's colour, the
   item's picture under it. */
.tip-name {
  display: block;
  margin-bottom: 6px;
  font-size: 13px;
  font-weight: 700;
  color: #ffffff;
  box-shadow: none !important;     /* the .grade-* frame belongs to grid cells */
  background: none;
}
.tip-name.grade-rare      { color: #8fb4ef; }
.tip-name.grade-excellent { color: #4fd36a; }
/* The game's own ancient name: green on the blue band, bold (bless card). */
.tip-name.grade-ancient   { color: #00ff00; background: #3c3cc8; font-weight: 700; padding: 1px 6px; text-shadow: none; }
.tip-img {
  display: block;
  max-width: 120px;
  max-height: 160px;
  margin: 4px auto 10px;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, .6));
}

/* ========================================================================== */
/* CHARACTER AND GUILD SHEETS                                                 */
/* ========================================================================== */

/* The two panes stack: since every page carries both side columns, the
   middle is too narrow for the sheet and the gear side by side. */
.char-two {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 22px;
}

.pane-title {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 0 0 10px;
  padding-bottom: 5px;
  border-bottom: 1px solid var(--panel-edge);
  font-family: var(--font-head);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .05em;
  color: #2a2620;
}
.char-pane .pane-title:not(:first-child) { margin-top: 18px; }
.pane-aside { margin-left: auto; font-family: var(--font-ui); font-size: 11px; color: var(--ink-soft); }

.char-top { display: grid; grid-template-columns: 140px minmax(0, 1fr); gap: 14px; }

.char-portrait img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  border: 1px solid var(--panel-edge);
  border-radius: 2px;
}

.char-guildbox {
  margin-top: 8px;
  padding: 7px 8px;
  text-align: center;
  font-family: var(--font-ui);
  font-size: 11px;
  background: rgba(255, 255, 255, .45);
  border: 1px solid var(--panel-edge);
  border-radius: 2px;
}
.char-guildbox b { display: block; margin: 4px 0 2px; }
.cg-sub { color: var(--ink-soft); font-size: 11px; }

/* the emblem, in the two sizes the pages need */
.cg-mark, .guild-mark-big {
  display: grid;
  grid-template-columns: repeat(8, 3px);
  grid-auto-rows: 3px;
  justify-content: center;
  outline: 1px solid rgba(0, 0, 0, .4);
}
.cg-mark { margin: 0 auto; }
.cg-mark i, .guild-mark-big i { display: block; width: 3px; height: 3px; }
.guild-mark-big { grid-template-columns: repeat(8, 7px); grid-auto-rows: 7px; }
.guild-mark-big i { width: 7px; height: 7px; }

.char-sheet { width: 100%; border-collapse: collapse; font-family: var(--font-ui); font-size: 11.5px; }
.char-sheet th {
  text-align: left;
  font-weight: 400;
  color: var(--ink-soft);
  padding: 4px 10px 4px 0;
  border-bottom: 1px solid rgba(206, 193, 178, .6);
  white-space: nowrap;
}
.char-sheet td {
  text-align: right;
  color: var(--ink);
  padding: 4px 0;
  border-bottom: 1px solid rgba(206, 193, 178, .6);
}
.char-sheet tr:last-child th, .char-sheet tr:last-child td { border-bottom: 0; }

.ch-name { font-size: 12.5px; }
.ch-gm { color: #7a3d10; font-weight: 700; }
.ch-pos { color: var(--ink-soft); }
.ch-on  { color: #2d8a4a; font-weight: 700; }
.ch-off { color: var(--ink-soft); }
.ch-fail { padding: 20px 0; text-align: center; color: var(--ink-soft); font-style: italic; }

.fold-btn {
  margin-left: auto;
  padding: 2px 10px;
  cursor: pointer;
  border: 1px solid var(--panel-edge);
  border-radius: 2px;
  background: rgba(255, 255, 255, .5);
  font-family: var(--font-ui);
  font-size: 11px;
  color: var(--ink-soft);
}
.fold-btn:hover { border-color: var(--ember); color: #7a3d10; }
.caret { display: inline-block; transition: transform .18s ease; }
.fold-btn[aria-expanded="true"] .caret { transform: rotate(180deg); }

.guild-top { display: flex; align-items: flex-start; gap: 14px; margin-bottom: 14px; }
.guild-id h3 { margin: 0 0 4px; font-size: 17px; }
.guild-notice {
  font-family: var(--font-ui);
  font-size: 11.5px;
  color: var(--ink-soft);
  line-height: 1.5;
}

@media (max-width: 900px) {
  .char-two { grid-template-columns: minmax(0, 1fr); gap: 18px; }
}
@media (max-width: 560px) {
  .char-top { grid-template-columns: minmax(0, 1fr); }
  .char-portrait { max-width: 180px; }
  .equip-grid {
    grid-template-columns: minmax(0, 1fr) 32px minmax(0, 1.25fr) 32px minmax(0, 1.15fr);
    grid-template-rows: 72px 140px 82px;
    gap: 6px;
    padding: 6px;
  }
  .eq-small { height: 32px; }
}

/* ========================================================================== */
/* DOWNLOAD                                                                   */
/* ========================================================================== */

/* Three steps, three tracks, one row - and they stack whole when the column
   cannot hold three.

   ⚠ NOT auto-fit. auto-fit fitted two and left the third sitting alone on a
   second row, which reads as a mistake rather than as a step. A fixed three
   with a media query is the only way to say "three or one, never two". */
.dl-steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 9px;
  margin: 16px 0 22px;
}
@media (max-width: 1180px) {
  .dl-steps { grid-template-columns: minmax(0, 1fr); }
}
/* Cut from the frame's material, with the site's chamfer, same as the fact
   tiles on About. Three pale rectangles in a row was the "cubes" problem. */
.dl-step {
  padding: 13px 14px 14px;
  font-family: var(--font-ui);
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--chrome-dim);
  background: linear-gradient(rgba(22, 40, 58, .96), rgba(10, 18, 28, .97));
  border: 1px solid rgba(141, 155, 176, .28);
  box-shadow:
    inset 0 1px 0 rgba(214, 221, 232, .10),
    0 5px 16px rgba(0, 0, 0, .24);
  clip-path: polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px);
}
/* ⚠ The heading row is given a height. One step's title wraps to two lines
   and the others do not, so without it the rule under each heading - and the
   paragraph below it - sat at three different heights across the row. */
.dl-step-h {
  display: flex;
  align-items: center;
  gap: 9px;
  /* Two lines of the heading face plus its leading: measured, not guessed.
     At 42 the two-line titles pushed past it and their paragraphs started
     5px lower than the one-line one. */
  min-height: 48px;
  margin-bottom: 8px;
  padding-bottom: 8px;
  border-bottom: 1px solid rgba(141, 155, 176, .18);
}
.dl-step b {
  color: var(--chrome);
  font-family: var(--font-head);
  font-size: 12.5px;
  letter-spacing: .03em;
}
.dl-step p { margin: 0; }
/* The step number as a cut tile rather than a circle: circles were the one
   shape nothing else on the page uses. */
.dl-n {
  flex: none;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border: 1px solid rgba(226, 145, 63, .5);
  background: rgba(226, 145, 63, .14);
  color: var(--ember-hot);
  font-family: var(--font-head);
  font-weight: 500;
  font-size: 12px;
}

/* The package box stays light - it is the thing being read, and a dark block
   this tall would take the page over - but it gets an ember edge on the left
   so it belongs to the same family as the headings above it. */
.dl-pack {
  margin-bottom: 14px;
  padding: 14px 16px;
  border: 1px solid var(--panel-edge);
  border-left: 3px solid rgba(141, 155, 176, .55);
  border-radius: 0 3px 3px 0;
  background: rgba(255, 255, 255, .45);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .55);
}
.dl-pack.is-primary {
  border-color: rgba(226, 145, 63, .45);
  border-left-color: var(--ember);
  background: rgba(226, 145, 63, .10);
}

.dl-pack-t {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 0 0 4px;
  font-family: var(--font-head);
  font-size: 14px;
  color: #2a2620;
}
.dl-badge {
  padding: 1px 7px;
  border: 1px solid var(--ember);
  border-radius: 2px;
  font-family: var(--font-ui);
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: #7a3d10;
}
.dl-meta { font-family: var(--font-ui); font-size: 11px; color: var(--ink-soft); margin-bottom: 6px; }
.dl-sum { font-family: var(--font-ui); font-size: 11.5px; line-height: 1.5; margin-bottom: 10px; }

.dl-mirrors { display: flex; flex-wrap: wrap; gap: 8px; }
.dl-mirror {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 13px;
  border: 1px solid #a8641f;
  border-radius: 3px;
  background: linear-gradient(#ffc070, #e2913f 55%, #b96f28);
  color: #4a2408;
  font-family: var(--font-ui);
  font-size: 11.5px;
  font-weight: 700;
}
.dl-mirror:hover { filter: brightness(1.08); }
/* A mirror with nothing uploaded to it yet is not a link. */
.dl-mirror.is-soon {
  border-color: #b9b0a4;
  background: rgba(0, 0, 0, .05);
  color: #8a8078;
  cursor: default;
}
.dl-go { font-size: 12px; }

.dl-reqs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 0 22px;
  font-family: var(--font-ui);
  font-size: 11.5px;
}
.dl-req { display: flex; justify-content: space-between; gap: 10px; padding: 4px 0; border-bottom: 1px solid rgba(206, 193, 178, .6); }
.dl-req span { color: var(--ink-soft); }

/* ========================================================================== */
/* STATISTICS                                                                 */
/* ========================================================================== */

.st-load { padding: 24px 0; text-align: center; font-family: var(--font-ui); color: var(--ink-soft); font-style: italic; }
.st-empty { font-family: var(--font-ui); font-size: 11.5px; color: var(--ink-soft); line-height: 1.55; font-style: italic; }

.st-table { width: 100%; border-collapse: collapse; font-family: var(--font-ui); font-size: 11.5px; }
.st-table th {
  text-align: left;
  font-weight: 400;
  color: var(--ink-soft);
  padding: 5px 10px 5px 0;
  border-bottom: 1px solid rgba(206, 193, 178, .6);
}
.st-table td {
  text-align: right;
  color: var(--ink);
  padding: 5px 0;
  border-bottom: 1px solid rgba(206, 193, 178, .6);
}
.st-table tr:last-child th, .st-table tr:last-child td { border-bottom: 0; }
.st-on { color: #2d8a4a; font-weight: 700; }
.st-mono { font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; }

.st-bars { font-family: var(--font-ui); font-size: 11.5px; }
.st-bar {
  display: grid;
  grid-template-columns: minmax(90px, 150px) minmax(0, 1fr) 62px 48px;
  align-items: center;
  gap: 10px;
  padding: 4px 0;
}
.sb-name { color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sb-track { height: 9px; border-radius: 2px; background: rgba(0, 0, 0, .09); overflow: hidden; }
.sb-track i { display: block; height: 100%; background: linear-gradient(90deg, #b96f28, #e2913f); }
.sb-num { text-align: right; color: var(--ink); }
.sb-pct { text-align: right; color: var(--ink-soft); }

.st-graph { width: 100%; height: auto; }
.st-graph-foot {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
  margin-top: 6px;
  font-family: var(--font-ui);
  font-size: 11px;
  color: var(--ink-soft);
}
.st-graph-foot b { color: var(--ink); }

.st-group { margin-bottom: 16px; }
.st-group-h {
  display: flex;
  justify-content: space-between;
  padding-bottom: 4px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--panel-edge);
  font-family: var(--font-head);
  font-size: 12px;
  color: #2a2620;
}
.st-census .is-zero { opacity: .45; }
.sc-who { display: block; font-size: 11px; color: var(--ink-soft); }

.st-missing {
  padding: 8px 0;
  border-bottom: 1px solid rgba(206, 193, 178, .6);
  font-family: var(--font-ui);
  font-size: 11.5px;
}
.st-missing:last-child { border-bottom: 0; }
.st-missing b { display: block; color: #2a2620; margin-bottom: 2px; }
.st-missing p { color: var(--ink-soft); line-height: 1.5; }

/* ========================================================================== */
/* GUIDES                                                                     */
/* ========================================================================== */

.guide-list {
  margin: 8px 0 14px;
  padding-left: 0;
  font-family: var(--font-ui);
  font-size: 12px;
  line-height: 1.6;
}
.guide-list li {
  position: relative;
  padding: 5px 0 5px 18px;
  border-bottom: 1px solid rgba(206, 193, 178, .6);
  color: var(--ink);
}
.guide-list li:last-child { border-bottom: 0; }
/* A diamond rather than a disc: the same shape the menu separators use, so
   the light column borrows one mark from the dark frame instead of
   introducing a bullet style of its own. */
.guide-list li::before {
  content: "";
  position: absolute;
  left: 3px;
  top: 11px;
  width: 6px;
  height: 6px;
  background: var(--ember);
  transform: rotate(45deg);
  opacity: .75;
}

/* A guide that is listed but not written. The menu says so rather than
   hiding it: the outline is what the page will be built on, and a section
   that quietly has four entries reads as a section nobody is working on. */
.rank-item.is-draft { color: #6c7a8d; }
.rank-item .soon {
  float: right;
  font-style: normal;
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  opacity: .7;
}

/* A term and its meaning: the legend under a guide's table. One grid, a
   fixed term column sized to the longest term in use ("Total MG / DL"), the
   definition wraps beside it; one font size for both so the rows read as
   one list, not a label column and a footnote column. */
.guide-defs {
  margin: 0 0 14px;
  font-family: var(--font-ui);
  font-size: 12px;
  line-height: 1.55;
}
.guide-defs > div {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr);
  gap: 0 14px;
  padding: 6px 0;
  border-bottom: 1px solid rgba(206, 193, 178, .6);
}
.guide-defs > div:last-child { border-bottom: 0; }
.guide-defs dt { color: #2a2620; font-weight: 700; }
.guide-defs dd { margin: 0; color: var(--ink); }

@media (max-width: 640px) {
  .guide-defs > div { grid-template-columns: minmax(0, 1fr); gap: 2px; }
}

/* ONE rhythm for every guide, whatever blocks it is built from: a heading
   sits 28px under what came before and 10px above its text; paragraphs,
   lists, notes and tables are 10px/14px apart. Scoped to the guide body so
   the rest of the parchment pages keep their own spacing. */
#guideBody h3 { margin: 28px 0 10px; }
#guideBody > h3:first-child, #guideBody .lead + h3 { margin-top: 24px; }
#guideBody p { margin: 0 0 10px; }
#guideBody .lead { margin-bottom: 14px; }
#guideBody .warn-note { margin: 14px 0; }
#guideBody .guide-list { margin: 0 0 14px; }
#guideBody .guide-list li { padding: 6px 0 6px 18px; }
#guideBody .guide-list li::before { top: 12px; }
#guideBody .guide-views, #guideBody .qt-tools { margin: 14px 0 10px; }
#guideBody .board-scroll, #guideBody .qt-scroll { margin: 0 0 10px; }
#guideBody .rs-legend { margin: 0 0 14px; }
#guideBody .guard { margin-top: 14px; }

/* Eight columns of numbers in a 548px column: the reset ladder fits when
   the cells are set at 6px sides instead of the sheet's 12px. */
.rs-table th, .rs-table td { padding-left: 6px; padding-right: 6px; }

/* The view switch above the reset ladder. The note beside it says what the
   second view is for, so the pair of buttons is not a puzzle. */
.guide-views {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 10px;
  margin: 18px 0 10px;
}
.view-note {
  font-family: var(--font-ui);
  font-size: 11px;
  color: var(--ink-soft);
}

/* The quest chain (guides.js, widget quest-table).

   The reference table, tidied: seven plain-text columns (#, Monster, Count,
   Hint, CP, Stats, Reward) in the site's own sheet style, nothing drawn in
   the cells, nothing printed for a zero.

   ⚠ FIXED layout, sized to the 548px column (546 inside the sheet border):
   30 + 136 + 54 + 86 + 42 + 48 + 150. Monster names stay on one line
   ("Golden Budge Dragon" bold is 128px); Hint and Reward fold when they must
   ("Swamp of Calmness", "Jewel of Bless Bundle x10"). Nothing scrolls
   sideways above phone width; below 560px the box scrolls and the table
   keeps its 546px. */
.qt-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
  font-family: var(--font-ui);
  font-size: 12px;
}
.qt-filter { display: inline-flex; align-items: center; gap: 8px; color: var(--ink-soft); }
.qt-filter input {
  width: 170px;
  max-width: 100%;
  min-height: 30px;
  padding: 4px 9px;
  border: 1px solid rgba(141, 155, 176, .5);
  border-radius: 2px;
  background: rgba(255, 255, 255, .55);
  font-family: var(--font-ui);
  font-size: 12.5px;
  color: var(--ink);
  outline: none;
}
.qt-filter input:focus { border-color: var(--ember); background: #fff; }
.qt-count { color: var(--ink-soft); }
.qt-board-link { margin-left: auto; color: var(--link); font-weight: 700; }

.qt-scroll { max-width: 100%; }
@media (max-width: 560px) {
  .qt-scroll { overflow-x: auto; }
  .sheet.qt-table { width: 546px; }
}

/* The sheet clips to its rounded corners with overflow: hidden, which also
   pins a sticky heading in place. This table keeps its corners on the cells
   instead and lets the heading travel. */
.sheet.qt-table {
  table-layout: fixed;
  width: 100%;
  overflow: visible;
}
.qt-table thead th {
  position: sticky;
  top: 0;
  z-index: 3;
  padding: 0 5px;
  height: 40px;
  font-size: 11px;
  letter-spacing: .04em;
  vertical-align: middle;
  white-space: nowrap;
  overflow: hidden;
}
.qt-table thead th:first-child { border-top-left-radius: 3px; }
.qt-table thead th:last-child { border-top-right-radius: 3px; }
.qt-table td {
  padding: 6px 6px;
  font-size: 12px;
  line-height: 1.4;
  vertical-align: middle;
  overflow-wrap: anywhere;
}
.qt-table th.qt-c-id,      .qt-table td.qt-c-id      { width: 30px; text-align: center; }
.qt-table th.qt-c-monster, .qt-table td.qt-c-monster { width: 136px; text-align: left; }
.qt-table th.qt-c-count,   .qt-table td.qt-c-count   { width: 54px; text-align: center; }
.qt-table th.qt-c-hint,    .qt-table td.qt-c-hint    { width: 86px; text-align: center; }
.qt-table th.qt-c-cp,      .qt-table td.qt-c-cp      { width: 42px; text-align: center; }
.qt-table th.qt-c-stats,   .qt-table td.qt-c-stats   { width: 48px; text-align: center; }
.qt-table th.qt-c-reward,  .qt-table td.qt-c-reward  { width: 150px; text-align: left; }
.qt-table td.qt-c-id, .qt-table td.qt-c-monster,
.qt-table td.qt-c-count, .qt-table td.qt-c-cp, .qt-table td.qt-c-stats { white-space: nowrap; }
.qt-table td.qt-c-monster { font-weight: 700; }
.qt-table td.qt-c-hint { color: var(--ink-soft); }

/* A sortable heading is a real button, 44px tall, focusable, named; the
   arrow is the same drawn one the other sheets use (.sheet th.is-sortable
   above), so the heading looks like every other sortable heading. */
.qt-sort {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  margin: 0 -4px;
  padding: 0 4px;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-shadow: inherit;
  cursor: pointer;
}
.qt-sort:hover { color: var(--ember-hot); }
.qt-sort:focus-visible { outline: 2px solid var(--ember-hot); outline-offset: -2px; border-radius: 2px; }
/* The narrow centred columns have no room for the sheet's 22px arrow
   gutter: the arrow sits in the top corner of the cell instead, and the
   heading keeps its 5px sides. */
.qt-table th.is-sortable { padding-right: 5px; }
.qt-table th.is-sortable::after { right: 2px; top: 8px; margin-top: 0; }

/* "Reward by class": the words, dotted, and the per-class list in a small
   dark plate under them. Shown on hover, on keyboard focus, and held open by
   a tap (guides.js toggles is-open) since a phone has no hover. */
.qt-byclass {
  position: relative;
  display: inline-block;
  color: var(--link);
  text-decoration: underline dotted;
  text-underline-offset: 3px;
  cursor: help;
}
.qt-byclass:focus-visible { outline: 2px solid var(--ember); outline-offset: 2px; border-radius: 2px; }
.qt-pop {
  display: none;
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  z-index: 5;
  padding: 6px 8px;
  white-space: nowrap;
  text-align: left;
  font-family: var(--font-ui);
  font-size: 12px;
  color: var(--chrome);
  background: rgba(8, 15, 25, .97);
  border: 1px solid rgba(226, 145, 63, .35);
  border-radius: 3px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .6);
}
.qt-byclass:hover .qt-pop,
.qt-byclass:focus-visible .qt-pop,
.qt-byclass.is-open .qt-pop { display: block; }
.qt-pop table { border-collapse: collapse; }
/* ⚠ Written at .sheet-zebra weight: the plate's own cells are table cells
   inside the sheet, and the zebra and hover rules for the sheet's tds would
   otherwise paint parchment stripes inside the dark plate. */
.sheet td .qt-pop tr th, .sheet td .qt-pop tr td,
.sheet tbody tr:hover td .qt-pop td { padding: 2px 6px; border: 0; background: none; }
.sheet td .qt-pop tr th { color: var(--ember-hot); font-weight: 700; text-align: left; letter-spacing: .04em; }
.sheet td .qt-pop tr td { color: var(--chrome); }
/* The last rows of the table open their plate downwards into the legend;
   the sheet must not clip it. */
.qt-table, .qt-table tbody, .qt-table td { overflow: visible; }
.qt-legend { font-size: 11.5px; }

@media (max-width: 560px) {
  .qt-tools { gap: 8px 12px; }
  .qt-filter { flex: 1 1 100%; }
  .qt-filter input { flex: 1 1 auto; width: auto; }
  .qt-board-link { margin-left: 0; }
}

/* ========================================================================== */
/* FORM PAGES                                                                 */
/* ========================================================================== */

/* A form is read down, not across. Held to a column and centred, with the
   fields as wide as the answers they take. */
.form-column {
  width: min(420px, 100%);
  margin: 0 auto;
  padding: 4px 0 8px;
}

.form-column .field { margin-bottom: 14px; }
.form-column .field label {
  display: block;
  margin-bottom: 5px;
  font-family: var(--font-ui);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: #4a423a;
}
.form-column .field input {
  padding: 9px 11px;
  font-size: 13px;
  background: #fdfaf6;
  border: 1px solid var(--panel-edge);
  border-radius: 3px;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .07);
}
.form-column .field input:focus {
  border-color: var(--ember);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .07), 0 0 0 3px rgba(226, 145, 63, .16);
}

.btn-wide { display: block; width: 100%; padding: 11px 0; font-size: 13px; }

/* The agreement boxes sit in their own bordered block, so they read as one
   thing to tick rather than two stray lines between the form and the button. */
.reg-agree {
  padding: 11px 12px;
  background: rgba(0, 0, 0, .035);
  border: 1px solid var(--panel-edge);
  border-radius: 3px;
}

.form-column .reg-captcha { justify-content: flex-start; }

.form-column .warn-note { margin-top: 22px; }

/* ========================================================================== */
/* ABOUT: a page that is read, not scanned                                    */
/* ========================================================================== */

/* The opening paragraph is set larger than the body and given room. It is the
   one paragraph everybody reads, and at body size it disappeared into the
   wall of text under it. */
.opening {
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.7;
  color: #2a2620;
  margin-bottom: 18px;
  padding-left: 14px;
  border-left: 3px solid var(--ember);
}
.opening b { color: #1d1a17; }

/* A numbered section. The number is a quiet marker in the margin rather than
   a heavy heading: it gives the page a spine to follow without competing with
   the words. */
.chapter { margin: 28px 0; }

/* The heading sits on a dark band the width of the column: the same navy the
   sidebar plates are cut from, so a section opens with a piece of the frame
   rather than with a hairline rule. */
.chapter h3 {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 14px;
  padding: 9px 14px;
  background: linear-gradient(90deg, rgba(22, 40, 58, .97), rgba(14, 26, 39, .90) 70%, rgba(14, 26, 39, 0));
  border-left: 3px solid var(--ember);
  color: var(--chrome);
  font-size: 14px;
  letter-spacing: .04em;
  text-shadow: 0 1px 0 #000;
}
/* The number is set in the ember, in a box of its own, so the eye can run
   down the page on the numbers alone. */
.chapter-n {
  flex: none;
  display: grid;
  place-items: center;
  min-width: 26px;
  height: 22px;
  padding: 0 5px;
  border: 1px solid rgba(226, 145, 63, .45);
  background: rgba(226, 145, 63, .12);
  font-family: var(--font-head);
  font-size: 11px;
  letter-spacing: .06em;
  color: var(--ember-hot);
}

/* One sentence lifted out of a section: the point of it, in the reader's way
   for a moment. */
/* A sentence lifted out of the section, cut from the dark material with the
   same chamfer as the fact tiles. */
.pull {
  position: relative;
  margin: 18px 0;
  padding: 16px 20px;
  font-family: var(--font-body);
  font-size: 14.5px;
  line-height: 1.65;
  color: var(--chrome);
  background: linear-gradient(rgba(22, 40, 58, .97), rgba(10, 18, 28, .97));
  border: 1px solid rgba(141, 155, 176, .28);
  border-left: 3px solid var(--ember);
  box-shadow: inset 0 1px 0 rgba(214, 221, 232, .09), 0 6px 20px rgba(0, 0, 0, .25);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%);
}

/* A picture with something to say under it. The caption sits on the frame
   rather than floating below it, so the pair reads as one object. */
.plate {
  margin: 20px 0;
  border: 1px solid var(--panel-edge);
  border-radius: 3px;
  overflow: hidden;
  background: var(--navy);
  box-shadow: 0 6px 20px rgba(0, 0, 0, .22);
}
.plate img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}
.plate figcaption {
  padding: 7px 12px;
  font-family: var(--font-ui);
  font-size: 11px;
  letter-spacing: .04em;
  color: var(--chrome-dim);
  background: linear-gradient(rgba(14, 26, 39, .95), rgba(7, 13, 21, .96));
}

.plate-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.plate-row .plate { margin: 20px 0; }
/* Portraits are taller than scenes; the row keeps them in proportion. */
.plate-row .plate img { aspect-ratio: 3 / 4; object-position: center 15%; }

@media (max-width: 560px) {
  .plate-row { grid-template-columns: 1fr; }
  .opening { font-size: 14px; }
}

/* ── VIP page ──────────────────────────────────────────────────────────────
   The three lengths on offer, side by side. Same plate-and-ember language as
   the vault pager (.vt-tab): this is the account area, not a shop of its own,
   and a fourth visual style here would read as a different site. */
.vip-buy {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 12px 0 4px;
}
.vip-opt {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 132px;
  padding: 10px 14px;
  font-family: var(--font-head);
  color: var(--chrome-dim);
  text-align: left;
  background: linear-gradient(rgba(38, 52, 74, .95), rgba(18, 27, 42, .95));
  border: 1px solid rgba(141, 155, 176, .4);
  border-radius: 3px;
  cursor: pointer;
  transition: color .15s ease, border-color .15s ease, filter .15s ease;
}
.vip-opt b { font-size: 15px; color: var(--ember-hot); }
.vip-opt span { font-size: 12px; }
.vip-opt:hover:not(:disabled) { filter: brightness(1.2); border-color: rgba(226, 145, 63, .75); }
/* Not enough coin: the option stays visible, because knowing what the next
   step costs is the point of showing it. */
.vip-opt:disabled { cursor: default; opacity: .5; }
.vip-opt.is-poor b { color: var(--chrome-dim); }

.vip-msg { margin-top: 8px; padding: 8px 10px; border-radius: 2px; }
.vip-msg.is-good { color: #1f5c2a; background: rgba(111, 174, 58, .18); }
.vip-msg.is-bad  { color: #7a2018; background: rgba(192, 57, 43, .14); }

/* ---- board variant tabs (Solo/Party) - viena izvelnes rinda, vairaki
   skati; poga tur pasu lapas tumso toni ar zelta aktivu malu ---- */
.board-tabs { display: flex; gap: 8px; margin: 0 0 10px; }
.board-tabs[hidden] { display: none; }
.board-tab {
  padding: 5px 18px;
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  color: var(--ink-soft);
  font: inherit;
  cursor: pointer;
  letter-spacing: .04em;
}
.board-tab:hover { border-color: var(--ember); color: var(--ink); }
.board-tab.active {
  border-color: var(--ember);
  color: #fff;
  background: var(--ember);
}

/* ---- board sub-filter pills (klases zem Solo/Party) - mazaks solis
   neka galvenie tabi, lai hierarhija nolasas no izmera vien ---- */
.board-subtabs { display: flex; gap: 6px; margin: 0 0 10px; flex-wrap: wrap; }
.board-subtabs[hidden] { display: none; }
.board-subtab {
  padding: 2px 12px;
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  color: var(--ink-soft);
  font: inherit;
  font-size: 11px;
  cursor: pointer;
  letter-spacing: .05em;
}
.board-subtab:hover { border-color: var(--ember); color: var(--ink); }
.board-subtab.active {
  border-color: var(--ember);
  color: #fff;
  background: var(--ember);
}

/* ---- Top 5 - Current Week lodzins virs globala dela.
   Lapa ir GAISA (pergaments) - virsraksti tumsa tinte, ne zelts. ---- */
.board-week { margin: 0 0 12px; }
.board-week[hidden] { display: none; }
.bw-title {
  color: var(--ink);
  letter-spacing: .08em;
  font-size: .9em;
  font-weight: 700;
  text-transform: uppercase;
  margin: 0 0 6px;
}
.bw-alltime { margin: 14px 0 0; }
.board-week .board-table { margin: 0; }

/* ---- komandas bloks (party runs): Wave/Time un vieta stav pa vidu
   pari visam biedru rindam, bloku skir plana linija. Zebras svitrojums
   bloka ieksa izslegts - puse komandas ar citu fonu lasijas ka kluda. ---- */
.board-table td[rowspan] { vertical-align: middle; }
.board-table td.td-span { text-align: center; }
.sheet tr.trun td { background: transparent; }
.board-table tr.run-first td { border-top: 1px solid var(--panel-edge); }

/* Read by screen readers and search engines, invisible on screen: every page
   carries its title as an h1 without adding a line to the layout. */
.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;
}


/* Random quests guide (High Templar Varian): one row per tier, rewards wrap. */
.rq-table { table-layout: fixed; }
.rq-table th:nth-child(1), .rq-table td.rq-range { width: 150px; }
.rq-table th:nth-child(2), .rq-table td.rq-stats { width: 150px; }
.rq-table td { white-space: normal; vertical-align: top; line-height: 1.5; }

/* Achievements page: one row per achievement, the reference server's shape -
   name with the level stars, the text with the current target, count/target. */
.ach-list { font-family: var(--font-ui); font-size: 12px; }
.ach-row { padding: 9px 0; border-bottom: 1px solid rgba(90, 70, 40, .18); }
.ach-row:last-child { border-bottom: 0; }
.ach-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.ach-head b { font-size: 13px; }
.ach-stars { letter-spacing: 2px; color: rgba(90, 70, 40, .35); }
.ach-star.is-on { color: var(--ember-hot); }
.ach-text { color: var(--ink-soft); margin-top: 2px; }
.ach-progress { display: flex; align-items: center; gap: 10px; margin-top: 4px; }
.ach-count { min-width: 120px; }
.ach-bar { flex: 1; height: 4px; background: rgba(90, 70, 40, .14); border-radius: 2px; overflow: hidden; }
.ach-bar i { display: block; height: 100%; background: linear-gradient(90deg, #e2913f, #ffc070); }
.ach-row.is-done .ach-bar i { background: #67b070; }
.ach-row.is-ready .ach-bar i { background: #67b070; }
.ach-claim { flex: none; padding: 3px 10px; font-size: 11px; }
.ach-note { flex: none; font-size: 11px; color: var(--ink-soft); font-style: italic; }
.ach-msg { margin: 4px 0 0; font-size: 11.5px; }
.ach-msg.is-ok { color: #3f7a48; }
.ach-msg.is-bad { color: #a33; }

/* ========================================================================== */
/* CRAFT SYSTEM AND VIRTUAL VAULT (craft.js, vault.js)                        */
/* ========================================================================== */

/* The bless craft page in this site's own dress: the same .sheet tables, the
   same buttons, the item names in the tooltip colours. Nothing new to learn. */
.cf-balance {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px 18px;
  margin: 4px 0 12px; font-family: var(--font-ui); font-size: 12.5px; color: var(--ink);
}
.cf-filters {
  text-align: center; margin: 0 0 10px; font-family: var(--font-ui); font-size: 11.5px; line-height: 1.8;
}
.cf-filter {
  padding: 0 2px; border: 0; background: none; cursor: pointer;
  font: inherit; color: var(--link);
}
.cf-filter.is-on { color: var(--ink); font-weight: 700; }
.cf-sep { color: var(--ember); margin: 0 4px; }
.cf-box { overflow-x: auto; }
.cf-table td, .cf-table th { text-align: center; }
.cf-table td:nth-child(2) { text-align: left; }
.cf-exc { color: #2d8a4a; }
.cf-name { cursor: help; }
.cf-name.grade-rare { color: #1b4b7a; }
.cf-table td.cf-ok { color: #2d8a4a; font-weight: 700; }
.cf-table td.cf-bad { color: #a33; font-weight: 700; }
.cf-link, .cf-held {
  padding: 0; border: 0; background: none; cursor: pointer;
  font-family: var(--font-ui); font-size: 12px; color: #2d8a4a;
}
.cf-held { color: var(--ink); text-decoration: underline dotted rgba(90, 80, 73, .5); }
.cf-link:hover, .cf-held:hover { color: #7a3d10; }
.cf-more {
  display: block; margin: 8px auto 0; padding: 0; border: 0; background: none; cursor: pointer;
  font-family: var(--font-ui); font-size: 12px; color: var(--ink);
}
.cf-more:hover { color: #7a3d10; }
.cf-warn { color: #a33; text-align: center; font-family: var(--font-ui); font-size: 12px; margin: 0 0 10px; }
.cf-acts { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 10px; }

/* The Create / Information card, opened in place of its row. */
.sheet tbody tr.cf-card-row:hover td { background: rgba(255, 255, 255, .34); }
.cf-card {
  position: relative; padding: 12px 18px 14px; text-align: center;
  font-family: var(--font-ui); font-size: 12px; line-height: 1.6;
}
.cf-card p { margin: 2px 0; }
.cf-x {
  position: absolute; top: 2px; right: 6px; padding: 0 4px; border: 0; background: none;
  cursor: pointer; font-size: 18px; line-height: 1; color: var(--ink-soft);
}
.cf-x:hover { color: #7a3d10; }
.cf-icon {
  display: inline-flex; align-items: center; justify-content: center; min-height: 64px; cursor: help;
}
.cf-icon img { max-height: 96px; filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .45)); }
.cf-title { font-size: 13px; }
.cf-costs { display: flex; justify-content: space-between; gap: 12px; margin: 8px 0; }
.cf-xtras { display: flex; justify-content: center; gap: 22px; margin: 8px 0; }
.cf-xtra { display: flex; flex-direction: column; align-items: center; }
.cf-xtra img { height: 32px; }
.cf-xtra.is-short { opacity: .45; }
.cf-mat { color: var(--ink-soft); }
.cf-card .btn-ghost { margin-top: 8px; min-width: 150px; }
.cf-card .btn-ghost:disabled { opacity: .5; cursor: not-allowed; }
.cf-out { margin: 10px 0 0; text-align: left; }

/* The special materials strip: icon, balance, name. */
.cf-strip { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px 18px; }
.cf-mat-ic {
  display: flex; flex-direction: column; align-items: center; width: 72px;
  font-family: var(--font-ui); font-size: 11px; text-align: center; color: var(--ink-soft);
}
.cf-mat-ic img { height: 36px; filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .45)); }
.cf-mat-ic b { font-size: 12.5px; color: var(--ink); }

/* Vault moves: the picked items wear the gold hover ring for good. */
.vt-move { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 10px; }
.vt-move .hint { margin: 0; }
.vt-move .btn-ghost:disabled { opacity: .5; cursor: not-allowed; }
.bag-grid .bag-item.is-movable { cursor: pointer; }
.bag-grid .bag-item.is-picked {
  background-color: rgba(226, 171, 63, .18);
  box-shadow: inset 0 0 0 2px rgba(255, 192, 112, .9);
}
.vv-item { display: inline-flex; align-items: center; gap: 8px; cursor: help; }
.vv-item img { width: 28px; height: 28px; object-fit: contain; filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .45)); }
.vv-table td, .vv-table th { text-align: center; }
/* the reference list's item orange. b.vv-name, so the generic b.grade-*
   colours and the .grade-* frame rings (meant for item boxes) stay off. */
b.vv-name { font-weight: 400; color: #df9b31; box-shadow: none; }
b.vv-name.grade-ancient { color: #455fdb; }

/* Events guide: the whole timetable (guides.js loadEvents). */
.ev-table .ev-name  { white-space: nowrap; font-weight: 700; }
.ev-table .ev-kind  { font-weight: 400; font-size: 11px; color: #7a6a55; margin-left: 4px; }
.ev-table .ev-times { line-height: 1.6; }

/* ---------------------------------------------------- phone-only menus --
   At phone width the right column lands under the page, so the account
   pages repeat the account links as one row on top of the content. */
.ac-mnav { display: none; }
@media (max-width: 860px) {
  .ac-mnav {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 0 0 16px;
  }
  .ac-mnav a {
    padding: 6px 10px;
    border: 1px solid var(--panel-edge);
    border-radius: 3px;
    background: rgba(255, 255, 255, .35);
    font-family: var(--font-ui);
    font-size: 12px;
    color: var(--link);
    text-decoration: none;
  }
  .ac-mnav a.is-active { background: var(--ember); border-color: var(--ember); color: #fff; }
}

/* VIP page: what VIP gives, one heading per group (vip.js drawGives). */
.vip-lines { margin: 4px 0 0; padding-left: 18px; color: var(--ink-soft); line-height: 1.55; }
.vip-lines li + li { margin-top: 3px; }
.vip-poor { flex-basis: 100%; margin: 8px 0 0; font-family: var(--font-ui); font-size: 12px; color: var(--ink-soft); }
/* two short columns: the timetable fits a phone without the side scroll */
@media (max-width: 560px) { .sheet.qt-table.ev-table { width: 100%; } }

/* live.js pauses the sparks of anything scrolled out of view. */
.fx-off::before, .fx-off::after,
.fx-off *, .fx-off *::before, .fx-off *::after { animation-play-state: paused !important; }

/* --------------------------------------------- in-page menus (parchment) --
   Rankings and guides carry their menu inside the page, so the side columns
   stay the same everywhere. Groups side by side, wrapping on narrow screens. */
.rank-board-menu,
.guide-menu {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 6px 18px;
  margin: 0 0 22px;
  padding: 12px 14px;
  border: 1px solid var(--panel-edge);
  background: rgba(255, 255, 255, .28);
  font-family: var(--font-ui);
}
.rank-board-menu .rank-group + .rank-group { margin-top: 0; }
.rank-board-menu .rank-group-t {
  margin: 0 0 4px;
  padding: 0 0 3px;
  border-bottom: 1px solid var(--panel-edge);
  font-family: var(--font-head);
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: #8a4f17;
  background: none;
}
.rank-board-menu .rank-item,
.guide-menu .rank-item {
  display: block;
  padding: 3px 0 3px 8px;
  border-left: 2px solid transparent;
  font-size: 12px;
  line-height: 1.35;
  color: var(--link);
  text-decoration: none;
  background: none;
}
.rank-board-menu .rank-item:hover,
.guide-menu .rank-item:hover { color: #8a4f17; border-left-color: var(--ember); }
.rank-board-menu a.rank-item.active,
.guide-menu a.rank-item.active { color: #1d1a17; font-weight: 700; border-left-color: var(--ember); background: none; }
.rank-board-menu .rank-item.is-soon { color: #8b8077; font-style: italic; }
.rank-board-menu .rank-item.is-soon::after { content: " soon"; font-size: 11px; font-style: normal; color: #a99d92; }
.rank-board-menu .rank-item.is-soon:hover { color: #5a5049; border-left-color: #cbbfb2; }
.guide-menu { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }

/* The reset gates inside the Resets guide (they used to be a sidebar block). */
.rs-gates-page li { display: block; color: var(--ink-soft); border-bottom-color: var(--panel-edge); }
.rs-gates-page b { color: var(--ink); font-weight: 700; }
.guide-index a { color: var(--link); }
.guide-index .gi-soon { font-weight: 400; font-style: italic; font-size: 11px; color: #8b8077; margin-left: 6px; }

/* Services: an entry that is not built yet, with what it will be. */
.sv-links li.is-soon { padding: 6px 4px; font-family: var(--font-ui); }
.sv-links .sv-soon { display: block; font-size: 12px; color: #8b8077; }
.sv-links .sv-soon i { font-style: normal; font-size: 11px; color: #a99d92; margin-left: 4px; }
.sv-links li.is-soon small { display: block; margin-top: 1px; font-size: 11px; color: #9a8f86; }
.ac-item.is-soon i { font-size: 11px; color: #4d5a6b; margin-left: 3px; }

/* Event Time: the name opens the event's line in the Events guide. */
.event-title a { color: inherit; text-decoration: none; }
.event-title a:hover { color: var(--ember-hot); }
.ev-table .ev-state { display: block; margin-top: 2px; font-size: 11px; font-weight: 700; letter-spacing: .04em; }
.ev-table .ev-state.is-on  { color: #2d7a3e; }
.ev-table .ev-state.is-off { color: #a33; }
.ev-table tr.is-off td { color: #8b8077; }
.ev-table .ev-about { font-size: 11.5px; color: var(--ink-soft); line-height: 1.45; }
.ev-table .ev-times { font-size: 11.5px; min-width: 150px; }
.ev-table tr.ev-split td { background: rgba(0, 0, 0, .06); font-family: var(--font-head); font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: #8a4f17; }
.ev-table tr.is-lit td { background: rgba(226, 145, 63, .22); }

/* Castle block: treasury, taxes, next siege (the reference server's
   Castle Treasury block). */
.siege-facts { margin: 10px 0 0; padding: 8px 0 0; border-top: 1px solid var(--rule); font-family: var(--font-ui); font-size: 11.5px; }
.siege-facts li { display: flex; justify-content: space-between; padding: 2px 0; color: var(--chrome-dim); }
.siege-facts em { font-style: normal; color: var(--chrome); }

/* About: the server at a glance and what is ours. */
.about-rates { margin-top: 6px; }
.about-list { margin: 8px 0 0; padding-left: 18px; font-family: var(--font-ui); font-size: 12.5px; line-height: 1.55; color: #2a2620; }
.about-list li + li { margin-top: 6px; }

/* Rankings and Guides: the menu is the first block of the left column; on a
   phone that column lands under the page, so a folding copy (.m-only) sits on
   top of the content instead and the column's own copy is hidden. */
.m-only { display: none; }
.rank-item.is-soon::after { content: " soon"; font-style: normal; font-size: 11px; opacity: .6; }
@media (max-width: 860px) {
  .m-only { display: block; }
  .block.is-menu { display: none; }
  .rank-fold { margin: 0 0 18px; }
  .rank-fold > summary {
    display: block;
    padding: 9px 12px;
    border: 1px solid var(--panel-edge);
    background: rgba(255, 255, 255, .35);
    font-family: var(--font-ui);
    font-size: 13px;
    color: var(--link);
    cursor: pointer;
  }
  .rank-fold[open] > summary { margin-bottom: 8px; }
}

/* ========================================================================== */
/* MARKET AND BONUSES (market.js, bonuses.js)                                 */
/* ========================================================================== */

/* The same tables and links as the Virtual vault; only the bits bless has
   on top: the Exc/Anc/Luck/currency checks, the Sell card, the earnings. */
.mk-soon { color: var(--ink-soft); cursor: help; }
.mk-soon i { font-size: 11px; }
.mk-checks {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 4px 14px;
  margin: 0 0 12px; font-family: var(--font-ui); font-size: 11.5px; color: var(--ink);
}
.mk-checks label { display: inline-flex; align-items: center; gap: 4px; cursor: pointer; }
.mk-checks input[type=search] {
  width: 150px; padding: 3px 6px; border: 1px solid var(--panel-edge); border-radius: 2px;
  background: rgba(255, 255, 255, .5); font: inherit;
}
.mk-table td.mk-price { white-space: nowrap; font-weight: 700; color: var(--ink); }
.mk-sell input, .mk-sell select {
  margin: 0 4px; padding: 3px 6px; border: 1px solid var(--panel-edge); border-radius: 2px;
  background: rgba(255, 255, 255, .6); font: inherit;
}
.mk-sell input { width: 120px; }
.mk-due {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 18px;
  font-family: var(--font-ui); font-size: 12.5px;
}
.bn-form { max-width: 360px; margin: 6px auto 14px; }
.bn-form .btn-primary:disabled { opacity: .5; cursor: not-allowed; }
.mk-own { font-family: var(--font-ui); font-size: 11.5px; color: var(--ink-soft); }

/* Donations (donate.js): one payment box as on bless, packages as radio rows. */
.dn-box {
  margin: 10px 0 6px; padding: 12px 16px 14px;
  border: 1px solid var(--panel-edge); border-radius: 3px; background: rgba(255, 255, 255, .32);
  font-family: var(--font-ui); font-size: 12.5px;
}
.dn-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.dn-head b { display: block; font-size: 13.5px; color: var(--ink); }
.dn-head span { color: var(--ink-soft); }
.dn-logos { display: flex; gap: 6px; }
.dn-logos img {
  width: 44px; height: 28px; padding: 4px 8px; box-sizing: border-box;
  background: #fff; border: 1px solid rgba(0, 0, 0, .12); border-radius: 4px; object-fit: contain;
}
.dn-packs { display: grid; gap: 4px; margin-bottom: 10px; }
.dn-pack { display: flex; align-items: center; gap: 8px; padding: 5px 8px; border-radius: 3px; cursor: pointer; }
.dn-pack:hover, .dn-pack.is-on { background: rgba(226, 145, 63, .14); }
.dn-extra { color: #2d8a4a; font-weight: 700; }
.dn-agree { display: flex; align-items: center; gap: 6px; margin: 4px 0 10px; cursor: pointer; }
.dn-pay { max-width: 320px; }
.dn-pay .btn-primary:disabled { opacity: .5; cursor: not-allowed; }
.dn-note { margin-top: 6px; }
.dn-policy { margin-top: 10px; font-family: var(--font-ui); font-size: 12px; color: var(--ink-soft); line-height: 1.55; }
.dn-policy b { color: var(--ink); }

/* Items market sidebar block (live.js fillMarket): name left in the market
   gold, price right, as on the reference server. */
.mk-side { font-family: var(--font-ui); font-size: 11.5px; }
.mk-side li {
  display: flex; justify-content: space-between; align-items: baseline; gap: 8px;
  padding: 5px 0; border-bottom: 1px solid rgba(36, 53, 74, .7);
}
.mk-side li:last-child { border-bottom: 0; }
.mk-side a {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: #e2c993; cursor: help;
}
.mk-side a:hover { color: var(--ember-hot); filter: none; }
.mk-side a.is-anc { color: #7289f8; }   /* bless: ancient names blue on dark */
.mk-side em { font-style: normal; color: var(--chrome-dim); white-space: nowrap; }
.mk-got { font-size: 11px; color: var(--ink-soft); }

/* Character page: the account's achievements and account information,
   under the inventory as on the reference character page. */
.ach-unlocked {
  margin-top: 6px; text-align: center; font-family: var(--font-ui); font-size: 12px;
  font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: #a4581c;
}
.char-acc { margin-top: 4px; }
.char-acc a { color: var(--link); }

/* Character inventory: centred under its heading, as the reference page
   centres its 8 x 8 bag. */
#bagGrid { margin: 0 auto; }

/* Balances in the account panel link to /bonuses but read as the numbers
   they are, in the panel's own colour. */
.ac-facts em a { color: inherit; }
.ac-facts em a:hover { color: var(--ember-hot); }

/* Lottery (lottery.js) and the Lottery Winners sidebar block (live.js). */
.lt-form { max-width: 460px; margin: 8px auto 12px; text-align: center; font-family: var(--font-ui); font-size: 12.5px; }
.lt-head { margin: 10px 0 4px; font-weight: 700; color: var(--ink); }
.lt-opts { display: flex; justify-content: center; flex-wrap: wrap; gap: 6px 18px; }
.lt-opts label { cursor: pointer; }
.lt-gift { margin-top: 10px; text-align: left; }
.lt-price { margin: 12px 0 8px; color: #2d8a4a; }
.lt-group { padding: 8px 0; border-bottom: 1px solid rgba(90, 70, 40, .18); font-family: var(--font-ui); font-size: 12px; }
.lt-group:last-child { border-bottom: 0; }
.lt-gh { display: flex; align-items: baseline; gap: 8px; }
.lt-list { margin: 4px 0 0; padding-left: 18px; columns: 2; color: var(--ink-soft); line-height: 1.5; }
.lt-group summary { cursor: pointer; color: var(--link); margin-top: 4px; }
@media (max-width: 560px) { .lt-list { columns: 1; } }
.lt-side { font-family: var(--font-ui); font-size: 11.5px; }
.lt-side li { padding: 5px 0; border-bottom: 1px solid rgba(36, 53, 74, .7); line-height: 1.4; }
.lt-side li:last-child { border-bottom: 0; }
.lt-side .lt-prize { display: block; color: #e2c993; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lt-side a { color: var(--chrome-dim); }
.lt-side em { float: right; font-style: normal; color: var(--chrome-dim); font-size: 11px; }
.lt-num {
  flex: none; padding: 1px 8px; border-radius: 2px; background: rgba(164, 88, 28, .14);
  color: #a4581c; font-weight: 700; font-size: 11.5px; letter-spacing: .03em;
}
.lt-gh b { color: var(--ink); font-size: 13px; }
.lt-about { margin: 3px 0 0; color: var(--ink-soft); line-height: 1.5; }
.lt-note { font-family: var(--font-ui); font-size: 12px; color: var(--ink); margin: 10px 0; }

/* ========================================================================== */
/* PLAYTEST (playtest.ejs, playtest.js): the front door during the playtest.  */
/* ========================================================================== */
.pt-hero {
  position: relative; margin: 0 0 18px; border-radius: 4px; overflow: hidden;
  background: linear-gradient(90deg, rgba(7, 13, 21, .92) 0%, rgba(7, 13, 21, .72) 48%, rgba(7, 13, 21, .15) 100%),
              url("/assets/img/gen/keyart/banner3.webp") center / cover no-repeat;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .35), inset 0 0 0 1px rgba(226, 145, 63, .35);
}
.pt-hero-in { position: relative; max-width: 660px; padding: 34px 34px 30px; color: var(--chrome); }
.pt-tag {
  display: inline-block; padding: 3px 10px; border: 1px solid rgba(255, 192, 112, .6); border-radius: 2px;
  font-family: var(--font-ui); font-size: 11px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ember-hot); background: rgba(226, 145, 63, .12);
}
.pt-title {
  margin: 12px 0 0; font-family: var(--font-head); font-size: 44px; line-height: 1.05; font-weight: 700;
  letter-spacing: .04em; color: #fff3df; text-shadow: 0 2px 14px rgba(0, 0, 0, .6);
}
.pt-sub { margin: 4px 0 12px; font-family: var(--font-head); font-size: 19px; color: var(--ember-hot); }
.pt-lead { max-width: 520px; margin: 0 0 16px; font-family: var(--font-body); font-size: 15px; line-height: 1.6; color: #e9e2d6; }
.pt-live { display: inline-flex; align-items: center; gap: 8px; margin-bottom: 18px; font-family: var(--font-ui); font-size: 12.5px; color: var(--chrome); }
.pt-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--chrome-dim); }
.pt-live.is-on .pt-dot { background: var(--online); box-shadow: 0 0 0 0 rgba(111, 174, 58, .7); animation: ptPulse 2s infinite; }
.pt-live.is-off .pt-dot { background: var(--offline); }
@keyframes ptPulse { 0% { box-shadow: 0 0 0 0 rgba(111, 174, 58, .6); } 70% { box-shadow: 0 0 0 8px rgba(111, 174, 58, 0); } 100% { box-shadow: 0 0 0 0 rgba(111, 174, 58, 0); } }
@media (prefers-reduced-motion: reduce) { .pt-live.is-on .pt-dot { animation: none; } }
.pt-cta { display: flex; flex-wrap: wrap; gap: 10px; }
.pt-btn {
  display: inline-block; padding: 11px 16px; border-radius: 4px; border: 1px solid rgba(214, 221, 232, .45);
  font-family: var(--font-ui); font-size: 13px; font-weight: 700; letter-spacing: .02em; text-decoration: none;
  color: var(--chrome); background: rgba(14, 26, 39, .7); transition: filter .15s ease, transform .15s ease;
}
.pt-btn:hover { filter: brightness(1.15); transform: translateY(-1px); color: #fff; }
.pt-btn-main { border-color: #a8641f; color: #4a2408; background: linear-gradient(#ffc070, #e2913f 55%, #b96f28); }
.pt-btn-main:hover { color: #3a1c06; }
.pt-btn-discord { border-color: #4a55c8; background: #4f5bd5; color: #fff; }
.pt-btn.is-off { opacity: .55; cursor: default; transform: none; filter: none; }
.pt-facts {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; margin: 0 0 22px; padding: 0; list-style: none;
  border: 1px solid var(--panel-edge); border-radius: 4px; background: rgba(255, 255, 255, .35);
}
.pt-facts li { padding: 12px 10px; text-align: center; border-left: 1px solid var(--panel-edge); }
.pt-facts li:first-child { border-left: 0; }
.pt-facts b { display: block; font-family: var(--font-head); font-size: 15px; color: #7a3d10; }
.pt-facts span { font-family: var(--font-ui); font-size: 11.5px; color: var(--ink-soft); }
.pt-steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin: 0; padding: 0; list-style: none; }
.pt-steps li {
  position: relative; padding: 18px 16px 16px; border: 1px solid var(--panel-edge); border-radius: 4px;
  background: rgba(255, 255, 255, .38); font-family: var(--font-ui); font-size: 12.5px;
}
.pt-num {
  display: inline-grid; place-items: center; width: 28px; height: 28px; margin-bottom: 8px; border-radius: 50%;
  background: linear-gradient(#ffc070, #e2913f 55%, #b96f28); color: #4a2408; font-weight: 700;
}
.pt-steps b { display: block; margin-bottom: 4px; font-size: 14px; color: var(--ink); }
.pt-steps p { margin: 0 0 8px; color: var(--ink-soft); line-height: 1.55; }
.pt-steps a { font-weight: 700; color: var(--link); }
.pt-soon { font-weight: 700; color: var(--ink-soft); }
.pt-features { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.pt-feat { padding: 14px; border-radius: 4px; background: rgba(255, 255, 255, .3); border: 1px solid rgba(206, 193, 178, .7); font-family: var(--font-ui); font-size: 12.5px; }
.pt-feat img { display: block; width: 44px; height: 44px; object-fit: contain; margin-bottom: 6px; filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .3)); }
.pt-feat b { display: block; margin-bottom: 4px; font-size: 13.5px; color: var(--ink); }
.pt-feat p { margin: 0; color: var(--ink-soft); line-height: 1.55; }
.pt-note { display: grid; grid-template-columns: 220px 1fr; gap: 16px; align-items: center; }
.pt-note img { width: 100%; border-radius: 4px; box-shadow: 0 4px 12px rgba(0, 0, 0, .3); }
.pt-faq { margin: 0; font-family: var(--font-ui); font-size: 12.5px; }
.pt-faq dt { margin-top: 10px; font-weight: 700; color: var(--ink); }
.pt-faq dt:first-child { margin-top: 0; }
.pt-faq dd { margin: 2px 0 0; color: var(--ink-soft); line-height: 1.55; }
@media (max-width: 860px) {
  .pt-steps, .pt-features { grid-template-columns: 1fr 1fr; }
  .pt-facts { grid-template-columns: 1fr 1fr; }
  .pt-facts li:nth-child(3) { border-left: 0; }
  .pt-facts li:nth-child(n+3) { border-top: 1px solid var(--panel-edge); }
}
@media (max-width: 560px) {
  .pt-hero-in { padding: 24px 18px 20px; }
  .pt-title { font-size: 34px; }
  .pt-steps, .pt-features, .pt-note { grid-template-columns: 1fr; }
  .pt-btn { flex: 1 1 100%; text-align: center; }
}
/* Step links as small buttons, no arrows. */
.pt-steps a, .pt-steps .pt-soon {
  display: inline-block; margin-top: 2px; padding: 5px 12px; border-radius: 3px;
  border: 1px solid #a8641f; background: rgba(226, 145, 63, .12);
  font-size: 12px; font-weight: 700; color: #7a3d10; text-decoration: none;
}
.pt-steps a:hover { background: rgba(226, 145, 63, .25); color: #4a2408; }
.pt-steps .pt-soon { border-color: var(--panel-edge); background: transparent; color: var(--ink-soft); }
.pt-steps li { display: flex; flex-direction: column; align-items: flex-start; }
.pt-steps li p { flex: 1; }
.discord .is-soon-btn { opacity: .55; cursor: default; }
/* Playtest changelog: four short groups side by side. */
.pt-log { display: grid; grid-template-columns: 1fr 1fr; gap: 14px 22px; font-family: var(--font-ui); font-size: 12.5px; }
.pt-log-group b { display: block; margin-bottom: 4px; font-family: var(--font-head); font-size: 14px; color: #7a3d10; }
.pt-log-group ul { margin: 0; padding-left: 18px; color: var(--ink-soft); line-height: 1.55; }
.pt-log-group li + li { margin-top: 2px; }
@media (max-width: 560px) { .pt-log { grid-template-columns: 1fr; } }

/* The one-pager has no menu bar, so nothing closes the top of the slab: the
   -4.5% overlap only slid the panel over the logo, and the sidebar padding
   that clears the bar left an empty band above Server Status. Here the slab
   sits under the logo and its first plates start at the top. */
.is-onepager .bottom-container { margin-top: 28px; }
.is-onepager .left-sidebar,
.is-onepager .right-sidebar { padding-top: 0; }
.is-onepager .left-sidebar  > .block:first-child .title-sidebar,
.is-onepager .right-sidebar > .block:first-child .title-sidebar { margin-top: -1px; }
.is-onepager .content { padding-top: 34px; }
/* On a phone the columns stack, and the status would land under the whole
   page. On the one-pager it is the first thing a visitor checks, so it leads. */
@media (max-width: 860px) {
  .is-onepager .left-sidebar { grid-row: 1; padding-top: 18px; }
  .is-onepager .content      { grid-row: 2; padding-top: 24px; }
}

/* The Discord card: their invite card, in our metal. Counts come from
   /api/discord (live.js); the dashes stand until they arrive. */
.dc-card {
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  margin: 2px 0 10px; padding: 14px 12px 12px;
  background: linear-gradient(rgba(88, 101, 242, .16), rgba(10, 18, 28, .6));
  border: 1px solid rgba(139, 155, 247, .35); border-radius: 6px;
  box-shadow: inset 0 1px 0 rgba(214, 221, 232, .08);
  text-decoration: none; color: var(--chrome);
  transition: border-color .2s ease, transform .2s ease;
}
.dc-card:hover { border-color: rgba(139, 155, 247, .75); transform: translateY(-1px); }
.dc-icon { border-radius: 14px; box-shadow: 0 4px 14px rgba(0, 0, 0, .55); }
.dc-name { font-family: var(--font-head); font-size: 15px; letter-spacing: .06em; color: var(--chrome); }
.dc-counts { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; color: var(--chrome-dim); }
.dc-stat { display: inline-flex; align-items: center; gap: 5px; }
.dc-stat b { color: var(--chrome); font-weight: 600; font-variant-numeric: tabular-nums; }
.dc-dot { width: 8px; height: 8px; border-radius: 50%; background: #80848e; }
.dc-dot.is-on { background: #23a55a; box-shadow: 0 0 6px rgba(35, 165, 90, .7); }
.dc-join {
  margin-top: 3px; padding: 7px 18px; border-radius: 4px;
  background: #5865f2; color: #fff; font-weight: 600; font-size: 12px;
}
.dc-card:hover .dc-join { background: #4752c4; }
