/* ===========================================================================
   Andromida DB v2 - design system
   ---------------------------------------------------------------------------
   Built on the tokens the existing site already established, so it still reads
   as the same database to players:

     background #222 -> a proper dark scale
     text       #ccc
     accent     #FC0 / #f7ef79  (gold)
     headings   Georgia serif

   Mobile-first: every layout rule starts at phone width and widens with
   min-width queries. No fixed pixel widths (the old style.css had .w680,
   .w800, .w330 classes that forced horizontal scrolling on a phone).
   =========================================================================== */

:root {
  /* --- surfaces (dark is the default, matching today's site) ------------- */
  --bg:            #1b1b1d;
  --surface:       #232326;
  --surface-2:     #2b2b2f;
  --surface-3:     #34343a;
  --border:        #3d3d44;
  --border-strong: #54545d;

  /* --- text -------------------------------------------------------------- */
  --text:          #d6d6d2;
  --text-dim:      #9a9a96;
  --text-faint:    #74746f;

  /* --- brand ------------------------------------------------------------- */
  --gold:          #ffcc00;
  --gold-soft:     #f7ef79;
  --gold-dim:      #a8880c;

  /* --- semantic ---------------------------------------------------------- */
  --good:          #6fcf78;
  --bad:           #ef5f5e;
  --info:          #6cb6e8;

  /* --- stat group accents (used as thin rails, never as text colour) ----- */
  --g-core:    #ffcc00;
  --g-element: #ff8a3d;
  --g-pen:     #e8617f;
  --g-res:     #6cb6e8;
  --g-attr:    #9b8cf0;
  --g-bane:    #6fcf78;
  --g-status:  #c98ad6;
  --g-misc:    #8a8a92;
  --g-detail:  #6b6b72;
  --g-other:   #6b6b72;

  /* --- type ------------------------------------------------------------- */
  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  --font-head: Georgia, "Times New Roman", Times, serif;
  --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* --- rhythm ----------------------------------------------------------- */
  --gutter: 16px;
  --radius: 8px;
  --radius-sm: 5px;
  --shadow: 0 2px 10px rgb(0 0 0 / 0.35);

  color-scheme: dark;
}

/* The database has always been dark, and players expect that, so dark is the
   base theme for everyone regardless of OS preference. Light is opt-in only,
   via data-theme="light" (a toggle can set it and persist it in localStorage). */
@media (prefers-color-scheme: light) {
  :root[data-theme="light-auto"] {
    --bg:            #f3f1ec;
    --surface:       #fffefb;
    --surface-2:     #eceae4;
    --surface-3:     #e0ddd5;
    --border:        #d3cfc5;
    --border-strong: #b5b0a3;
    --text:          #26251f;
    --text-dim:      #5d5b52;
    --text-faint:    #8b887d;
    --gold:          #9a7500;
    --gold-soft:     #7d5f00;
    --gold-dim:      #c9a63a;
    --shadow: 0 2px 10px rgb(0 0 0 / 0.10);
    color-scheme: light;
  }
}
:root[data-theme="light"] {
  --bg: #f3f1ec; --surface: #fffefb; --surface-2: #eceae4; --surface-3: #e0ddd5;
  --border: #d3cfc5; --border-strong: #b5b0a3;
  --text: #26251f; --text-dim: #5d5b52; --text-faint: #8b887d;
  --gold: #9a7500; --gold-soft: #7d5f00; --gold-dim: #c9a63a;
  --shadow: 0 2px 10px rgb(0 0 0 / 0.10);
  color-scheme: light;
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.5 var(--font-body);
  overflow-x: hidden;           /* nothing should ever scroll sideways */
}

h1, h2, h3, h4 { font-family: var(--font-head); font-weight: 600; margin: 0 0 .4em; line-height: 1.2; }
h1 { font-size: 1.5rem; }
h2 { font-size: 1.25rem; }
h3 { font-size: 1.05rem; }

a { color: var(--gold); text-decoration: none; }
a:hover, a:focus-visible { text-decoration: underline; }

:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* =========================================================== layout ====== */

.wrap {
  width: 100%;
  max-width: 1400px;
  margin: 0 auto;
  padding: 0 var(--gutter);
}

/* ===========================================================================
   The header: the old site's, rebuilt.
   ===========================================================================
   A Granado Espada banner with the logo on it, the version and build date at
   the top right, and a translucent brass bar of sections carrying their row
   counts. The two background images are the originals out of the old site's
   images/ folder, in images/chrome/ here (prolog.webp resized from 8000px wide
   to 2400 - the original was 1.3 MB for a strip nobody sees at full size).

   The bar is sticky and the banner is not: scrolling leaves the navigation
   where a visitor can reach it, and takes the decoration away. That is the one
   deliberate departure from the original, which scrolled the whole thing off.
   ========================================================================= */

.site-header {
  position: relative;
  z-index: 40;
  background: #0b0a08 url('../../images/chrome/prolog.webp') no-repeat center top;
  background-size: cover;
}
/* The logo, at the size the original wore it: about two thirds of the window,
   capped at its own 900px so it is never upscaled into mush.
 *
 * The banner is SHORTER than the logo on purpose. The artwork is 900x297 and
 * most of the bottom third is the "G" flourish's tail, which the original let
 * run off the bottom behind the brass bar - that overlap is why the header
 * reads as a masthead rather than as a picture with a menu under it. So the
 * anchor is a fixed height with overflow hidden, and the height is derived from
 * the logo's own width so the crop is the same proportion at every size.
 *
 * 0.72 of the logo's height is not arbitrary: the gold "Andromida" ends at 0.61
 * of the artwork and the flourish runs on to 0.96, so 0.72 keeps every readable
 * part with room to spare and cuts only the tail. At 0.64 that fit on a desktop
 * and guillotined "Andromida" on a phone, where the same proportion is a dozen
 * pixels rather than forty. 0.72 / 3.0303 (the aspect ratio) = 0.2376. */
.site-header__logo {
  --logo-w: min(900px, 68vw);
  display: block;
  /* The floor matters on a phone: below ~300px of logo the proportional crop
     is a dozen pixels, and the bar's own inset highlight eats the last of
     them, so "Andromida" ends up sitting under the brass. */
  height: max(72px, calc(var(--logo-w) * 0.2376));
  padding: 0;
  overflow: hidden;
  text-align: center;
  line-height: 0;
  border: 0;
}
.site-header__logo img {
  width: var(--logo-w); height: auto;
  /* A shade of the top margin trimmed, so the script sits where it did. */
  margin-top: calc(var(--logo-w) / -112);
  filter: drop-shadow(0 2px 6px rgb(0 0 0 / .8));
}
.site-header__meta {
  position: absolute; top: 10px; right: 14px;
  text-align: right; font-size: .72rem; line-height: 1.5;
  color: var(--text-dim);
  text-shadow: 0 1px 3px #000;
}
.site-header__meta a { color: var(--gold-dim); }

/* --- the brass bar ------------------------------------------------------- */

.site-nav {
  position: sticky; top: 0; z-index: 41;
  background: linear-gradient(to bottom, rgb(52 54 44 / .92), rgb(31 28 20 / .96));
  box-shadow: inset 0 3px 8px rgb(255 255 255 / .18),
              inset 0 -3px 8px rgb(255 255 255 / .18),
              0 2px 10px rgb(0 0 0 / .5);
  backdrop-filter: blur(6px);
}
.site-nav__row {
  display: flex; align-items: center; gap: 6px;
  min-height: 40px;
}
.site-nav__inner {
  display: flex; align-items: center; gap: 2px;
  flex: 1 1 auto; min-width: 0;
}
.site-nav a {
  display: flex; align-items: center; gap: 6px;
  padding: 9px 13px;
  color: #ffcc00; font-size: .92rem; white-space: nowrap;
  border: 0; border-radius: 0;
  transition: background-color .25s ease;
}
.site-nav a span { color: rgb(255 255 255 / .45); font-size: .82em; }
.site-nav a:hover { background: rgb(0 0 0 / .45); text-decoration: none; }
.site-nav a[aria-current="page"] { background: rgb(0 0 0 / .45); }

/* The "NEW" flag on a section that has just appeared.
   A <b>, not a <span>, so the dim count-bubble rule above does not claim it.
   A tinted outline rather than a filled badge, and no animation: it only has
   to be the one warm thing on a bar of gold text, which the eye finds without
   being shouted at. It rides high like a superscript so it reads as a note on
   the label rather than part of it. Driven by GE_NAV_NEW in partials.php. */
.nav-new {
  margin-left: -1px;
  align-self: flex-start;
  padding: 1px 3px;
  background: rgb(224 85 60 / .16);
  border: 1px solid rgb(255 130 95 / .32);
  color: #e8886a;
  font: 600 .48rem/1 var(--font-body, sans-serif);
  letter-spacing: .06em;
  border-radius: 2px;
}

/* The same flag inside the More menu, which is a gold panel with dark text -
   the warm outline that reads as "new" against a dark bar is nearly invisible
   against gold, so it darkens instead of lightening. Same shape, same weight,
   same amount of attention; only the contrast flips. */
.site-nav__menu .nav-new {
  background: rgb(150 40 20 / .1);
  border-color: rgb(130 45 25 / .4);
  color: #8e3318;
}
/* …and flips back on hover, where the item itself goes dark. */
.site-nav__menu a:hover .nav-new {
  background: rgb(224 85 60 / .18);
  border-color: rgb(255 130 95 / .36);
  color: #e8886a;
}

/* The mark on "More" itself. Not the word NEW a second time: "More" has not
   changed, something inside it has, and that is what a notification dot says
   in a language everyone already reads. It sits tight against the label and
   rides high, like the badge it is standing in for. */
.nav-dot {
  width: 5px; height: 5px; flex: none;
  align-self: flex-start; margin: 2px 0 0 1px;
  background: #e8886a; border-radius: 50%;
  box-shadow: 0 0 0 2px rgb(224 85 60 / .2);
}

/* The icon set, inline SVG so there is no icon font to fetch. */
.ge-ico { width: 15px; height: 15px; flex: none; fill: currentColor; opacity: .9; }

.site-nav__burger { display: none; }

@media (max-width: 560px) { .site-nav a { padding: 8px 9px; font-size: .84rem; } }

/* Portraits are taller than item icons. */
.card__icon.portrait { width: 44px; height: 56px; }
.detail__head .card__icon.portrait { flex: none; }

.grade[data-grade="cash"]        { background: var(--g-pen);  color: #1b1b1d; }
.grade[data-grade="recruit"]     { background: var(--g-res);  color: #1b1b1d; }
.grade[data-grade="basic"]       { background: var(--g-bane); color: #1b1b1d; }
.grade[data-grade="accomplish"]  { background: var(--gold);   color: #1b1b1d; }

/* The search sits at the right end of the brass bar, with the magnifier inside
   the field. The old site had a magnifier that linked to a separate
   search.php; this one searches as you type, so the glyph is decoration and
   the field is the control. */
.quick-search {
  position: relative; flex: 1 1 auto; min-width: 0;
  margin-left: auto; max-width: 340px;
  display: flex; align-items: center;
}
.quick-search > .ge-ico {
  position: absolute; left: 9px;
  color: #ffcc00; opacity: .65; pointer-events: none;
}
.quick-search input {
  width: 100%;
  padding: 6px 10px 6px 29px;
  background: rgb(0 0 0 / .4);
  border: 1px solid rgb(255 204 0 / .28);
  border-radius: 2px;
  color: var(--text);
  font: inherit; font-size: .88rem;
}
.quick-search input:focus {
  outline: none;
  border-color: rgb(255 204 0 / .7);
  background: rgb(0 0 0 / .6);
}
.quick-search input::placeholder { color: rgb(255 255 255 / .38); }
.quick-search__results {
  position: absolute; inset-inline: 0; top: calc(100% + 6px);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  max-height: min(60vh, 420px);
  overflow-y: auto;
  z-index: 50;
}
.quick-search__results:empty { display: none; }
.quick-search__results a {
  display: flex; justify-content: space-between; gap: 10px;
  padding: 9px 12px; border-bottom: 1px solid var(--border);
  color: var(--text);
}
.quick-search__results a:last-child { border-bottom: 0; }
.quick-search__results a:hover,
.quick-search__results a[aria-selected="true"] { background: var(--surface-3); text-decoration: none; }
.quick-search__results small { color: var(--text-faint); white-space: nowrap; }

.layout {
  display: grid;
  /* minmax(0, 1fr), not 1fr: a grid item's automatic minimum is its content's,
     so one wide child - a panel with a max-width, a long table - pushes the
     column past the viewport and gives the page a horizontal scrollbar with
     nothing at the end of it. The desktop rule below has always said
     minmax(0, ...) for the results column; the phone layout needs it too. */
  grid-template-columns: minmax(0, 1fr);
  gap: 18px;
  padding: 18px 0 40px;
  align-items: start;
}
/* .layout's padding shorthand overrides the .wrap gutter on the same element,
   which on a desktop is right - the sidebar and the results run to the edge of
   the 1400px column, as the old site's did. On a phone there is no sidebar in
   the flow, so it just put the headings and the cards hard against both edges
   of the screen. The gutter comes back at the width where that happens. */
@media (max-width: 899px) {
  .layout { padding-left: var(--gutter); padding-right: var(--gutter); }
}
@media (min-width: 900px) {
  .layout { grid-template-columns: 290px minmax(0, 1fr); }
}

/* ========================================================== filters ====== */

/* On a phone the filter panel is a slide-over sheet; on a desktop it is a
   sticky sidebar. Same markup, no duplication. */
/* The old site's search column: a dark panel inside a thin brass frame, with a
   gold bar per section. The frame and the type scale come from the game UI
   (same tokens as the item and stance panels further down this file) rather
   than from the generic card styling, because this column sits beside those
   panels and the two should read as one screen. */
.filters {
  background: var(--ge-bg, var(--surface));
  /* Near-black, with the same corner the crest slice already has. The frame
     follows the artwork's curve instead of cutting across it; see --ge-radius.
     The title bar is rounded to match, which is the half that was missing the
     first time and left a lit notch at each end of it. */
  border: 1px solid #241f14;
  border-radius: var(--ge-radius);
  padding: 0;
}
@media (max-width: 899px) {
  .filters {
    position: fixed; inset: 0 0 0 auto;
    width: min(92vw, 380px);
    z-index: 60;
    border-radius: 0;
    overflow-y: auto; overflow-x: hidden;   /* see the desktop rule below */
    transform: translateX(100%);
    transition: transform .22s ease;
    box-shadow: var(--shadow);
  }
  .filters[data-open="true"] { transform: none; }
  .scrim {
    position: fixed; inset: 0; z-index: 55;
    background: rgb(0 0 0 / .55);
    opacity: 0; pointer-events: none; transition: opacity .22s ease;
  }
  .scrim[data-open="true"] { opacity: 1; pointer-events: auto; }
}
@media (min-width: 900px) {
  /* Clears the sticky section bar (40px) with a little air.
   *
   * overflow-x has to be named, not left to `auto` by implication: the title
   * bar overhangs the panel by 1px on each side so its crests cover the frame
   * corners, which makes the content 2px wider than the box and gives a
   * horizontal scrollbar across the bottom of the sidebar. There is nothing
   * along that axis worth scrolling to. */
  .filters {
    position: sticky; top: 52px; max-height: calc(100vh - 70px);
    overflow-y: auto; overflow-x: hidden;
  }
  .filters__close { display: none; }
}

/* The gold title bar with the crest at each end. These are the client's own
   slices - titleleft/titlemid/titleright - which is why it looks like the game
   rather than like a CSS gradient pretending to.
 *
 * The bar sits INSIDE the frame, not overhanging it. It used to overhang by
 * 1px so the crests covered the frame corners, which is what the original did
 * (`left: -2px`) - but that only mattered while the frame was bright brass.
 * Against the near-black frame it buys nothing, and it made the panel content
 * 2px wider than the panel, which put a horizontal scrollbar along the bottom
 * of the sidebar. */
.ge-title, .filters__head {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  height: 27px; padding: 0 34px;
  /* Rounded with the panel, never on its own: a square bar inside a rounded
     frame is what produced the notch the comment above remembers.
     overflow has to be named as well - the crests are ::before/::after, and a
     border-radius clips the element's own background but not its children, so
     without this the two 30px slices would keep their square corners and the
     rounding would be invisible exactly where it matters. */
  border-radius: var(--ge-radius) var(--ge-radius) 0 0;
  overflow: hidden;
  background: #2b2416 url('../../images/chrome/titlemid.jpg') repeat-x center;
  font-family: var(--font-head);
  font-size: .92rem; font-weight: 600;
  color: #f7ef79;
  text-shadow: 0 1px 2px #000;
}
.ge-title::before, .filters__head::before,
.ge-title::after,  .filters__head::after {
  content: ''; position: absolute; top: 0;
  width: 30px; height: 27px;
}
.ge-title::before, .filters__head::before {
  left: 0; background: url('../../images/chrome/titleleft.jpg') no-repeat left top;
}
.ge-title::after, .filters__head::after {
  right: 0; background: url('../../images/chrome/titleright.jpg') no-repeat right top;
}
.filters__close {
  position: absolute; right: 4px; top: 0;
  background: none; border: 0; color: #f7ef79;
  font-size: 1.3rem; line-height: 27px; cursor: pointer; padding: 0 6px;
  z-index: 1;
}

/* --- one section ---------------------------------------------------------
   Rendered as <details>, so the summary IS the clickable gold bar and the
   browser owns the open state. `fieldset.facet` is still styled the same way:
   the monster, map and shop pages build their facets with ui.js's facetGroup(),
   which emits a fieldset, and both shapes have to look identical in the same
   column. */
details.facet, fieldset.facet {
  border: 0; border-bottom: 1px solid var(--ge-frame-2, var(--border));
  padding: 0; margin: 0;
}
details.facet:last-child, fieldset.facet:last-child { border-bottom: 0; }

/* The section bar is the same brass the nav bar is made of.
 *
 * It used to be the client's tab strip (tabmid.gif) stretched to height, which
 * was a different metal from everything around it: a flat olive band with pale
 * cream type, sitting directly under a nav bar of lit brass with gold type and
 * directly above a panel of neither. Three treatments in fifteen vertical pixels
 * is what read as "not arranged with the rest".
 *
 * So it borrows the nav's gradient, its two inset highlights and its #ffcc00,
 * and the column now reads as one piece of furniture from the menu down. The
 * highlights are inset box-shadows rather than borders so they do not add height
 * - a facet list is a dozen of these stacked, and a pixel each is a row. */
.facet__head, fieldset.facet > legend {
  display: flex; align-items: center; gap: 8px;
  width: 100%; box-sizing: border-box;
  padding: 7px 11px;
  background: linear-gradient(to bottom, rgb(52 54 44 / .92), rgb(31 28 20 / .96));
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .14),
              inset 0 -1px 0 rgb(255 255 255 / .07),
              0 1px 4px rgb(0 0 0 / .45);
  border-top: 0;
  border-bottom: 1px solid #000;
  font-family: var(--font-head);
  font-size: .88rem; letter-spacing: .02em;
  color: #ffcc00;
  text-shadow: 0 1px 2px #000;
  cursor: pointer;
  user-select: none;
  transition: background-color .2s ease;
}
.facet__head::-webkit-details-marker { display: none; }
.facet__head { list-style: none; }
/* The nav bar darkens on hover rather than brightening; same here, so the two
   respond to the pointer the same way. */
.facet__head:hover, fieldset.facet > legend:hover { background-image: none; background-color: rgb(0 0 0 / .45); }
details.facet[open] > .facet__head { color: #ffe066; }
/* The category glyph. Dimmer than the label at rest so the bar still reads as a
   row of words, and lit with it on hover and while the panel is open - the icon
   is there to be recognised at a glance, not to compete with the name. */
.cat-ico {
  width: 15px; height: 15px; flex: none;
  fill: currentColor; opacity: .62;
  transition: opacity .2s ease;
}
.facet__head:hover .cat-ico,
details.facet[open] > .facet__head .cat-ico { opacity: 1; }
.start__cat .cat-ico { width: 17px; height: 17px; opacity: .8; }
.start__cat:hover .cat-ico { opacity: 1; }

.facet__title { flex: 1 1 auto; min-width: 0; }
.facet__count {
  color: var(--ge-dim, var(--text-faint)); font-size: .78em;
  font-variant-numeric: tabular-nums;
}

/* The chevron: a CSS triangle, so there is no icon font and no SVG to load. */
.facet__head::before {
  content: ''; flex: none;
  width: 0; height: 0;
  border: 4px solid transparent;
  border-left-color: currentColor;
  transition: transform .15s ease;
}
details.facet[open] > .facet__head::before { transform: rotate(90deg) translateX(1px); }

.facet__body {
  display: flex; flex-direction: column; gap: 0;
  max-height: 300px; overflow-y: auto;
  padding: 4px 0;
  background: var(--ge-bg-2, var(--surface));
}
.facet__body--grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 2px; }

.check {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 10px;
  cursor: pointer; font-size: .875rem;
  color: var(--ge-text, var(--text));
  min-height: 28px;
}
.check:hover { background: color-mix(in srgb, var(--ge-gold, #e3c265) 10%, transparent); }
.check input { accent-color: var(--ge-gold-2, var(--gold)); width: 14px; height: 14px; flex: none; }
.check span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.check input:checked + .check__label { color: var(--ge-gold, var(--gold)); }
.check em, .check__n {
  margin-left: auto; color: var(--ge-dim, var(--text-faint));
  font-style: normal; font-size: .8em; font-variant-numeric: tabular-nums;
}

.range { display: flex; align-items: center; gap: 6px; margin: 0; padding: 3px 10px; }
.range label { flex: 1 1 auto; font-size: .85rem; color: var(--ge-text, var(--text-dim)); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.range input {
  width: 62px; flex: none; padding: 4px 6px; font: inherit; font-size: .82rem;
  background: var(--ge-bg, var(--surface-2)); border: 1px solid var(--ge-frame-2, var(--border));
  border-radius: 2px; color: var(--ge-text, var(--text));
}
.range input:not(:placeholder-shown) { border-color: var(--ge-gold-2, var(--gold-dim)); }

/* Touch targets are tight by design here - the old column was dense and that
   is half of why it was quick to use - so give them back on a phone, where
   the panel is a full-height sheet and has the room. */
@media (max-width: 899px) {
  .check { min-height: 36px; font-size: .92rem; }
  .facet__body { max-height: none; }
}

/* Light mode. The --ge-* tokens are deliberately fixed - inside an item tooltip
   they carry meaning and must not flip - but this column is site chrome, not a
   tooltip, so here the brass frame is re-stated in light values instead. */
:root[data-theme="light"] .filters,
:root[data-theme="light-auto"] .filters {
  background: var(--surface); border-color: var(--border-strong);
}
:root[data-theme="light"] .filters__head,
:root[data-theme="light-auto"] .filters__head,
:root[data-theme="light"] .facet__head,
:root[data-theme="light-auto"] .facet__head,
:root[data-theme="light"] fieldset.facet > legend,
:root[data-theme="light-auto"] fieldset.facet > legend {
  background-image: none;
  background-color: var(--surface-3); color: var(--gold-soft);
  box-shadow: none;
  border-bottom-color: var(--border);
}
:root[data-theme="light"] .facet__body,
:root[data-theme="light-auto"] .facet__body,
:root[data-theme="light"] .facet__list,
:root[data-theme="light-auto"] .facet__list {
  background: var(--surface);
}
:root[data-theme="light"] .check,
:root[data-theme="light-auto"] .check { color: var(--text); }
:root[data-theme="light"] details.facet,
:root[data-theme="light-auto"] details.facet,
:root[data-theme="light"] fieldset.facet,
:root[data-theme="light-auto"] fieldset.facet { border-bottom-color: var(--border); }

.btn {
  display: inline-flex; align-items: center; gap: 6px; justify-content: center;
  padding: 8px 14px; min-height: 38px;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  font: inherit; cursor: pointer;
}
.btn:hover { background: var(--surface-3); }
.btn--primary { background: var(--gold); color: #1b1b1d; border-color: var(--gold); font-weight: 600; }
.btn--primary:hover { background: var(--gold-soft); }
.btn--ghost { background: none; border-color: var(--border); color: var(--text-dim); }

/* ========================================================== results ====== */

.toolbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin-bottom: 12px;
}
.toolbar__count { color: var(--text-dim); font-size: .9rem; margin-inline-end: auto; }
.toolbar select {
  padding: 7px 10px; min-height: 38px;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm); font: inherit;
}

.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.chips:empty { display: none; }
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 6px 4px 10px; font-size: .82rem;
  background: var(--surface-2); border: 1px solid var(--border-strong);
  border-radius: 999px; color: var(--text-dim);
}
.chip button {
  background: none; border: 0; color: var(--text-faint);
  cursor: pointer; font-size: 1rem; line-height: 1; padding: 0 2px;
}
.chip button:hover { color: var(--bad); }

.cards { display: grid; grid-template-columns: 1fr; gap: 10px; }
@media (min-width: 620px)  { .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1150px) { .cards { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 12px;
  cursor: pointer;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 10px;
  text-align: left;
  width: 100%;
  font: inherit;
  color: inherit;
}
.card:hover { border-color: var(--border-strong); border-left-color: var(--gold); background: var(--surface-2); }
.card[aria-current="true"] { border-left-color: var(--gold); background: var(--surface-2); }

/* No background: the artwork is chroma-keyed and transparent everywhere it is
   not the subject, so a filled square behind it draws a grey tile around every
   portrait instead of letting it sit on the card. */
.card__icon {
  width: 44px; height: 44px; flex: none; position: relative;
  border-radius: var(--radius-sm);
  display: grid; place-items: center; overflow: hidden;
  font-size: .62rem; font-weight: 700; letter-spacing: .03em;
  color: var(--text-faint); text-transform: uppercase;
}
/* The sprite is painted over the abbreviation, so a missing sheet degrades to
   a readable type label instead of an empty grey square. */
.card__icon i {
  position: absolute; inset: 0;
  background-repeat: no-repeat;
  transform-origin: top left;
  transform: scale(var(--icon-scale, 1));
}

.card__name { font-weight: 600; font-size: .98rem; line-height: 1.25; overflow-wrap: anywhere; }
.card__meta { color: var(--text-dim); font-size: .8rem; margin-top: 3px;
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; }
.card__stats {
  grid-column: 1 / -1;
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 4px; margin-top: 4px;
}

/* A pill is a flex row, not a line of inline boxes.
   It was three inline things - a ::before dot nudged with `vertical-align: 1px`,
   a label, and a bold number - each sitting on the text baseline at a different
   height, so a row of pills had dots that stepped up and down and numbers that
   did not line up with their own labels. One pixel of vertical-align cannot fix
   that, because the dot has no baseline to nudge FROM: it is a 6px block in a
   .76rem line box. Centring the three on the cross axis is exact at any font
   size, and the gap replaces the margin so the spacing is one number. */
.stat-pill {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: .76rem; line-height: 1.5; padding: 2px 8px; border-radius: 999px;
  background: var(--surface-2); border: 1px solid var(--border);
  color: var(--text-dim); white-space: nowrap;
}
.stat-pill b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.stat-pill[data-group]::before,
.stat-pill[data-stat]::before {
  content: ""; flex: none; width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--g-other);
}
.stat-pill[data-group="core"]::before    { background: var(--g-core); }
.stat-pill[data-group="element"]::before { background: var(--g-element); }
.stat-pill[data-group="pen"]::before     { background: var(--g-pen); }
.stat-pill[data-group="res"]::before     { background: var(--g-res); }
.stat-pill[data-group="attr"]::before    { background: var(--g-attr); }
.stat-pill[data-group="bane"]::before    { background: var(--g-bane); }
.stat-pill[data-group="status"]::before  { background: var(--g-status); }
.stat-pill[data-group="misc"]::before    { background: var(--g-misc); }
.stat-pill[data-group="detail"]::before  { background: var(--g-detail); }

/* The six primary character stats keep the old site's colours, which players
   read by hue before they read the label. They are set on the DOT, not on the
   text: #330066 and #3300ff are almost black against this background and would
   be unreadable as type, but as a 6px marker beside a light label they are the
   colours people know. A ring lifts the two darkest off the pill. */
.stat-pill[data-stat]::before { box-shadow: 0 0 0 1px rgb(255 255 255 / .22); }
.stat-pill[data-stat="STR"]::before { background: #ff6600; }  /* orange */
.stat-pill[data-stat="AGI"]::before { background: #ffff00; }  /* yellow */
.stat-pill[data-stat="CON"]::before { background: #cc0000; }  /* red, shown as HP  */
.stat-pill[data-stat="DEX"]::before { background: #3300ff; }  /* blue */
.stat-pill[data-stat="INT"]::before { background: #00cc00; }  /* green */
.stat-pill[data-stat="CHA"]::before { background: #330066; }  /* purple, shown as SEN */

/* --- the label palette --------------------------------------------------
   Everything else a card can show - a monster's resistances, a map's counts, a
   raid's ratings - keyed by the LABEL rather than by a group, so "Fire" is the
   same red on every page it appears on and a card is scannable by colour.

   Seven hues, all from one family (Material 200/300), so they sit together at
   6px on a dark pill and none of them shouts. Within a single card no two
   labels share one:

     monster   HP green · EXP amber · Fire red · Ice cyan · Lgt violet · Psy pink
     map       Monsters orange · NPCs cyan · Baron red · Stalls amber
     raid      Rewards amber · A.R. orange · D.R. cyan

   The five elemental ones are the values Yanuck specified; the rest follow them
   so the set reads as one palette. */
.stat-pill[data-stat="EXP"]::before,
.stat-pill[data-stat="Stalls"]::before,
.stat-pill[data-stat="Rewards"]::before   { background: #ffd54f; }  /* amber   */
.stat-pill[data-stat="Fire"]::before,
.stat-pill[data-stat="Baron"]::before     { background: #ff5252; }  /* red     */
.stat-pill[data-stat="Ice"]::before,
.stat-pill[data-stat="NPCs"]::before,
.stat-pill[data-stat="D.R."]::before      { background: #64d8ff; }  /* cyan    */
.stat-pill[data-stat="Lgt"]::before       { background: #b388ff; }  /* violet  */
.stat-pill[data-stat="Psy"]::before       { background: #f48fb1; }  /* pink    */
.stat-pill[data-stat="HP"]::before        { background: #81c784; }  /* green   */
.stat-pill[data-stat="Monsters"]::before,
.stat-pill[data-stat="A.R."]::before      { background: #ff8a65; }  /* orange  */

.grade {
  font-size: .72rem; text-transform: uppercase; letter-spacing: .04em;
  padding: 1px 6px; border-radius: 3px;
  background: var(--surface-3); color: var(--text-dim);
}
/* Grand Master is the top of the ladder (EnchantLv 156), so it outranks the
   gold High Master wears: a pale rim on dark, which reads as "above gold"
   without inventing a brighter gold that would fight the brass chrome. */
.grade[data-grade="Grand Master"] {
  background: #241d10; color: var(--gold, #ffd76a);
  box-shadow: inset 0 0 0 1px var(--gold, #ffd76a);
  font-weight: 700;
}
.grade[data-grade="High Master"] { background: var(--gold); color: #1b1b1d; font-weight: 600; }
.grade[data-grade="Master"]      { background: var(--gold-dim); color: #16160f; }
.grade[data-grade="Expert"]      { background: #5a5a63; color: #eceae4; }

.empty, .loading {
  padding: 40px 16px; text-align: center; color: var(--text-dim);
  border: 1px dashed var(--border); border-radius: var(--radius);
}

/* --- the "pick a filter" screen -----------------------------------------
   The item page's opening state. Not a dashed empty box: nothing has gone
   wrong here, the page is waiting for a decision, so it looks like a panel
   rather than like a hole. */
.start {
  grid-column: 1 / -1; min-width: 0;
  padding: 34px 22px;
  background: var(--ge-bg-2, var(--surface));
  border: 1px solid var(--ge-frame, var(--border-strong));
  border-radius: var(--radius);
  text-align: center;
}
.start__title {
  font-family: var(--font-head);
  font-size: 1.25rem; color: var(--ge-gold, var(--gold));
  margin: 0 0 8px;
}
.start__lead {
  max-width: min(54ch, 100%); margin: 0 auto 22px;
  color: var(--text-dim); font-size: .92rem; line-height: 1.55;
}
.start__grid {
  /* 176px, not 150: the button carries an icon, a name and a count, and at 150
     the two longest names ("Accessory", "Material") were ellipsised down to
     "Access...". The grid still drops to two columns on a phone and one on a
     narrow one. */
  display: grid; grid-template-columns: repeat(auto-fill, minmax(176px, 1fr));
  gap: 8px; max-width: min(740px, 100%); margin: 0 auto;
}
.start__cat {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 13px; min-height: 42px;
  background: linear-gradient(to bottom, rgb(52 54 44 / .92), rgb(31 28 20 / .96));
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .14);
  border: 1px solid var(--ge-frame-2, var(--border));
  border-radius: var(--radius-sm);
  color: #ffcc00; font: inherit; font-size: .92rem; cursor: pointer;
  transition: background-color .2s ease, border-color .2s ease;
}
.start__cat:hover {
  background-image: none; background-color: rgb(0 0 0 / .5);
  border-color: var(--ge-gold, var(--gold));
}
.start__catname { flex: 1 1 auto; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.start__cat em {
  flex: none; font-style: normal; font-size: .8em;
  color: rgb(255 255 255 / .45); font-variant-numeric: tabular-nums;
}

.pager { display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 18px; }
.pager span { color: var(--text-dim); font-size: .9rem; }

/* =========================================================== detail ====== */

/* Bottom sheet on a phone, right-hand drawer on a desktop. */
.detail {
  position: fixed; z-index: 70;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow);
  overflow-y: auto;
  transition: transform .24s ease;
}
@media (max-width: 899px) {
  .detail {
    inset: auto 0 0 0; max-height: 88vh;
    border-radius: var(--radius) var(--radius) 0 0;
    transform: translateY(101%);
  }
  .detail[data-open="true"] { transform: none; }
}
@media (min-width: 900px) {
  .detail {
    inset: 0 0 0 auto; width: min(560px, 46vw);
    transform: translateX(101%);
  }
  .detail[data-open="true"] { transform: none; }
}
/* A stance panel is five skill columns wide; 560px turns each of them into a
   40-character-per-line ribbon. Pages that show one get a wider drawer. */
@media (min-width: 1100px) {
  .detail--wide { width: min(1000px, 72vw); }
}
.detail__head {
  position: sticky; top: 0; z-index: 2;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: 14px 16px;
  display: flex; align-items: flex-start; gap: 12px;
}
.detail__head h2 { margin: 0; font-size: 1.15rem; }
.detail__head .meta { color: var(--text-dim); font-size: .85rem; margin-top: 3px; }
.detail__close {
  margin-left: auto; background: none; border: 0; color: var(--text-dim);
  font-size: 1.7rem; line-height: 1; cursor: pointer; padding: 0 4px;
}
.detail__body { padding: 14px 16px 40px; }
.detail__body > section { margin-bottom: 20px; }
.detail__body h3 {
  font-size: .82rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--text-faint); font-family: var(--font-body); font-weight: 700;
  border-bottom: 1px solid var(--border); padding-bottom: 5px; margin-bottom: 8px;
}
.detail .desc { color: var(--text-dim); font-size: .92rem; overflow-wrap: anywhere; }
.detail .desc span { font-weight: 600; }

.kv { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 8px; }
.kv > div {
  background: var(--surface-2); border-radius: var(--radius-sm);
  padding: 7px 10px; border-left: 2px solid var(--border-strong);
}
.kv dt { font-size: .74rem; color: var(--text-faint); text-transform: uppercase; letter-spacing: .04em; }
.kv dd { margin: 1px 0 0; font-weight: 600; font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere; }

.stat-table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.stat-table th, .stat-table td { text-align: left; padding: 5px 8px; border-bottom: 1px solid var(--border); }
.stat-table td:last-child { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
.stat-table tbody tr:hover { background: var(--surface-2); }
.stat-table caption {
  text-align: left; color: var(--text-faint); font-size: .76rem;
  text-transform: uppercase; letter-spacing: .05em; padding: 8px 8px 3px;
}

.ing { display: flex; align-items: center; gap: 8px; padding: 5px 0; border-bottom: 1px solid var(--border); font-size: .9rem; }
.ing:last-child { border-bottom: 0; }
.ing b { margin-left: auto; font-variant-numeric: tabular-nums; }

.piece-list { display: flex; flex-wrap: wrap; gap: 6px; }
.piece-list a { font-size: .84rem; padding: 3px 9px; background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 999px; }

/* ============================================================ misc ======= */

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

.skeleton {
  background: linear-gradient(90deg, var(--surface) 25%, var(--surface-2) 37%, var(--surface) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.3s ease-in-out infinite;
  border-radius: var(--radius);
  height: 86px;
}
@keyframes shimmer { 0% { background-position: 100% 0 } 100% { background-position: 0 0 } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

.site-footer {
  border-top: 1px solid var(--border);
  padding: 18px 0 30px; color: var(--text-faint); font-size: .84rem;
}

/* ===========================================================================
   Sections added after the items/characters pilot: monsters, maps, shops and
   the reference pages. Everything below reuses the tokens defined at the top,
   so light mode and dark mode both come out right without a second set of
   rules - the pilot's mistake was defining light as the base and then patching
   dark on top, which meant every new component had to be fixed twice.
   ========================================================================= */

/* --- skip link and the More menu ---------------------------------------- */

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 60;
  background: var(--gold); color: #1b1b1d; padding: 10px 16px; font-weight: 600;
}
/* :focus-visible, not :focus. The back-to-top button hands focus back to this
   link so the keyboard returns to the top of the page with the view, and with
   plain :focus that made the gold "Skip to results" box flash into the corner
   for someone who had just clicked the button with a mouse. :focus-visible is
   the browser's own answer to "is a keyboard driving this?", so the link still
   appears the moment anyone tabs to it and stays out of the way otherwise. */
.skip:focus-visible { left: 0; }

/* The More menu. The old site opened its dropdowns on hover, which is a trap on
   a touchscreen, so this is a <details>: the browser handles the open state and
   the keyboard, and a tap opens it. */
.site-nav__more { position: relative; }
.site-nav__more > summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; gap: 5px;
  padding: 9px 13px;
  color: #ffcc00; font-size: .92rem; white-space: nowrap;
  transition: background-color .25s ease;
}
.site-nav__more > summary::-webkit-details-marker { display: none; }
.site-nav__more > summary .caret {
  width: 0; height: 0;
  border: 4px solid transparent; border-top-color: currentColor;
  margin-top: 3px;
}
.site-nav__more > summary:hover,
.site-nav__more[open] > summary { background: rgb(0 0 0 / .45); }
.site-nav__menu {
  position: absolute; left: 0; top: 100%; z-index: 42;
  min-width: 210px; padding: 0;
  background: rgb(255 204 0 / .94);
  box-shadow: 0 4px 12px rgb(0 0 0 / .6);
  display: flex; flex-direction: column;
}
.site-nav__menu a {
  white-space: nowrap; color: #1b1b1d; padding: 8px 14px;
  font-size: .9rem; transition: background-color .25s ease;
}
.site-nav__menu a:hover { background: rgb(0 0 0 / .78); color: #ffcc00; text-decoration: none; }

/* The Filters button lives on the bar at every width, beside the search on a
   desktop and beside the ☰ on a phone. */
.btn--filters {
  flex: none; display: inline-flex; align-items: center; gap: 6px;
  min-height: 30px; padding: 5px 12px;
  background: rgb(0 0 0 / .4);
  border: 1px solid rgb(255 204 0 / .35);
  border-radius: 2px;
  color: #ffcc00; font-size: .88rem;
}
.btn--filters:hover { background: rgb(0 0 0 / .65); border-color: rgb(255 204 0 / .7); }
.btn--filters .ge-ico { width: 13px; height: 13px; }
@media (min-width: 900px) { .btn--filters { display: none; } }

/* On a phone the section links become a stack behind the ☰ button, and the
   dropdown would hang off the edge, so it becomes a plain inline list. */
@media (max-width: 860px) {
  /* The version and build date sit over the logo once the banner narrows; the
     old site dropped them on a phone for the same reason. */
  .site-header__meta { display: none; }
  .site-header__logo { padding: 14px 0 18px; }

  .site-nav__burger {
    display: inline-flex; align-items: center; justify-content: center; flex: none;
    background: rgb(0 0 0 / .4); border: 1px solid rgb(255 204 0 / .35);
    color: #ffcc00; font-size: 1.1rem; line-height: 1;
    min-height: 30px; padding: 4px 11px; border-radius: 2px; cursor: pointer;
  }
  .site-nav__burger[aria-expanded="true"] { background: rgb(0 0 0 / .7); }

  /* The row keeps ☰ and Filters on one line; the links drop below it, which is
     why the stack is absolutely positioned rather than pushing the row apart. */
  .site-nav__row { flex-wrap: wrap; padding-top: 5px; padding-bottom: 5px; }
  .site-nav__inner {
    display: none; order: 3; flex-basis: 100%;
    flex-direction: column; align-items: stretch;
    padding: 6px 0 2px;
    border-top: 1px solid rgb(255 204 0 / .2);
    margin-top: 5px;
  }
  .site-nav[data-open="true"] .site-nav__inner { display: flex; }
  .site-nav a, .site-nav__more > summary { justify-content: center; padding: 10px; }
  .quick-search { max-width: none; margin: 6px 0 2px; }
  .site-nav__menu { position: static; box-shadow: none; min-width: 0; }
}

/* --- facet list (the generic name ui.js emits) --------------------------- */

.facet__list {
  display: flex; flex-direction: column; gap: 0;
  max-height: 300px; overflow-y: auto;
  padding: 4px 0;
  background: var(--ge-bg-2, var(--surface));
}
.facet__more { display: flex; flex-direction: column; gap: 0; }
.facet__toggle {
  align-self: flex-start; background: none; border: 0; padding: 4px 10px;
  color: var(--ge-gold-2, var(--gold-soft)); font: inherit; font-size: .82rem; cursor: pointer;
}
.facet__toggle:hover { text-decoration: underline; }
.check--tri { gap: 8px; }
.check--tri select {
  flex: none; width: 74px; padding: 4px 6px; font: inherit; font-size: .84rem;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.range__input {
  width: 74px; flex: none; padding: 6px; font: inherit; font-size: .85rem;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); color: var(--text);
}
.range__input:not(:placeholder-shown) { border-color: var(--gold-dim); }

/* --- chips and pager (button form) -------------------------------------- */

.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; font: inherit; font-size: .82rem; cursor: pointer;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: 999px;
}
.chip:hover { border-color: var(--bad); }
.chip__x { color: var(--text-faint); }
.chip:hover .chip__x { color: var(--bad); }

.pager { flex-wrap: wrap; gap: 4px; }
.pager__btn {
  min-width: 38px; min-height: 38px; padding: 0 10px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font: inherit; cursor: pointer; font-variant-numeric: tabular-nums;
}
/* The current page is excluded, or hovering it repaints it as an ordinary
   button - `:hover:not(:disabled)` outranks `[aria-current]` on specificity, so
   the page you are on stopped looking like the page you are on the moment the
   pointer crossed it. */
.pager__btn:hover:not(:disabled):not([aria-current="page"]) { background: var(--surface-3); }
.pager__btn:disabled { opacity: .4; cursor: default; }
.pager__btn[aria-current="page"] {
  background: var(--gold); border-color: var(--gold); color: #1b1b1d; font-weight: 700;
}
/* First and last: the same size, marked out by colour so the strip reads as
   "jump" | "step" | pages | "step" | "jump" rather than as nine equal buttons. */
.pager__btn--end { color: var(--gold-soft, var(--gold)); font-weight: 700; }
.pager__gap { color: var(--text-faint); padding: 0 2px; align-self: center; }

/* --- tags --------------------------------------------------------------- */

.tag {
  display: inline-block; margin-left: 6px; padding: 1px 7px;
  font-size: .7rem; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
  border-radius: 999px; background: var(--surface-3); color: var(--text-dim);
  vertical-align: middle;
}
.tag--boss   { background: var(--g-element); color: #1b1b1d; }
.tag--raid   { background: var(--g-pen);     color: #1b1b1d; }
.tag--barter { background: var(--g-attr);    color: #1b1b1d; }
.tag--muted  { background: var(--surface-3); color: var(--text-faint); }
.tag[data-kind="debuff"] { background: var(--bad);  color: #1b1b1d; }
.tag[data-kind="buff"]   { background: var(--good); color: #1b1b1d; }

/* Race, map-type and currency reuse the grade swatch, so a card's leading
   badge always looks the same wherever it appears. */
.grade[data-grade="Wildlife"] { background: var(--g-bane);    color: #1b1b1d; }
.grade[data-grade="Human"]    { background: var(--g-res);     color: #1b1b1d; }
.grade[data-grade="Demon"]    { background: var(--g-pen);     color: #1b1b1d; }
.grade[data-grade="Undead"]   { background: var(--g-attr);    color: #1b1b1d; }
.grade[data-grade="Golem"]    { background: var(--g-misc);    color: #fffefb; }
.grade[data-grade="Misc"]     { background: var(--surface-3); color: var(--text-dim); }
.grade[data-grade="City"]     { background: var(--g-core);    color: #1b1b1d; }
.grade[data-grade="Field"]    { background: var(--g-bane);    color: #1b1b1d; }
.grade[data-grade="Mission"]  { background: var(--g-res);     color: #1b1b1d; }
.grade[data-grade="Vis"]      { background: var(--g-core);    color: #1b1b1d; }
.grade[data-grade="Feso"]     { background: var(--g-element); color: #1b1b1d; }
.grade[data-grade="Mixed"]    { background: var(--g-status);  color: #1b1b1d; }
.grade[data-grade="none"]     { background: var(--surface-3); color: var(--text-faint); }
.grade--inline { margin-left: 6px; }

.card__icon.portrait[data-race] { font-weight: 700; }
.cards--tight { gap: 6px; }

/* --- map thumbnails ----------------------------------------------------- */

.card__icon.thumb { width: 80px; height: 44px; padding: 0; overflow: hidden; position: relative; }
.card__icon.thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.thumb__fallback {
  position: absolute; inset: 0; display: grid; place-items: center;
  color: var(--text-faint); font-size: .74rem; z-index: -1;
}
.map-image-wrap { margin-bottom: 18px; }
.map-image {
  width: 100%; max-width: 492px; height: auto; display: block;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface-2);
}

/* --- detail panel additions -------------------------------------------- */

.detail__section { margin-bottom: 20px; }

.facts { display: grid; grid-template-columns: max-content 1fr; gap: 4px 14px; margin: 0; font-size: .9rem; }
.facts dt { color: var(--text-faint); }
.facts dd { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
/*
 * Pet cards.
 *
 * `.facts--inline` lived here: a grid of 160px columns that the pet card put
 * all of its fields through. A grid flows <dt> and <dd> as independent cells,
 * so a label could finish one row while its value started the next and the
 * card read a slot out of step. Pets use the plain two-column .facts list now,
 * which keeps each label beside its own value and still stacks on a phone.
 */
.pet-eff  { display: inline; }
.pet-id   { font-family: var(--font-mono); font-size: .82rem; font-weight: 400; color: var(--text-faint); }
.pet-abil { display: inline-flex; flex-wrap: wrap; gap: 4px; vertical-align: middle; }
.pet-abil span {
  padding: 1px 7px; border-radius: 999px; background: var(--surface-3); color: var(--text-dim);
  font-size: .7rem; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
}
/* What the buff grants, under its name - the hover card's text, always shown. */
.pet-buff-desc {
  margin-top: 1px; font-weight: 400; font-size: .86rem; color: var(--text-dim);
  overflow-wrap: anywhere;
}

.grid { width: 100%; border-collapse: collapse; font-size: .89rem; }
.grid th, .grid td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--border);
  vertical-align: top; }
.grid th {
  font-size: .72rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--text-faint); font-weight: 700; white-space: nowrap;
}
.grid tbody tr:hover { background: var(--surface-2); }
.grid td:not(:first-child) { font-variant-numeric: tabular-nums; white-space: nowrap; }
/* `td.small`, not `.small`: the nowrap rule above is `.grid td:not(:first-child)`,
   which carries a pseudo-class and so outweighs a plain `.grid .small`. The
   list of monsters in a drop summary therefore never wrapped, and a zone with
   four long names pushed a horizontal scrollbar across the whole panel.
   Matching the element as well makes the two rules equal and lets source order
   decide, which is what was intended all along. */
.grid td.small {
  white-space: normal;
  font-size: .82rem; color: var(--text-dim);
  /* A single unbroken name longer than the column still has to give way. */
  overflow-wrap: anywhere;
}

/* And the table itself may not outgrow its panel. `auto` layout widens a table
   to fit its widest cell no matter what the container says; `fixed` makes the
   columns share what there is and the text wrap inside them. Only the drop
   table gets a column width, because only it has a column whose job is to be
   narrow - the chance never wraps, so the monster list takes the remainder. */
.grid--drops { table-layout: fixed; width: 100%; }
.grid--drops th:nth-child(2), .grid--drops td:nth-child(2) { width: 9.5rem; }

.inline-item { display: inline-flex; align-items: center; gap: 6px; }
.icon-sm {
  width: 22px; height: 22px; flex: none; position: relative; overflow: hidden;
  border-radius: 3px; background: var(--surface-2);
  font-size: .58rem; color: var(--text-faint); display: grid; place-items: center;
}
.icon-sm i {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%) scale(calc(var(--icon-scale, 1) * 0.5));
  background-repeat: no-repeat;
}

.plain { list-style: none; margin: 0; padding: 0; font-size: .9rem; }
.plain li { padding: 4px 0; border-bottom: 1px solid var(--border); }
.plain li:last-child { border-bottom: 0; }

.linkrow { display: flex; flex-wrap: wrap; gap: 6px; }
.pill-link {
  font-size: .84rem; padding: 4px 10px; cursor: pointer;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: 999px; font-family: inherit;
}
.pill-link:hover { border-color: var(--gold-dim); text-decoration: none; }
.pill-link--flat { cursor: default; color: var(--text-dim); }
.pill-link--flat:hover { border-color: var(--border); }

h4.sub {
  font-size: .76rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--text-faint); margin: 10px 0 6px;
}

.rules { list-style: none; margin: 0; padding: 0; display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 2px 12px; font-size: .88rem; }
.rule { display: flex; align-items: center; gap: 7px; padding: 3px 0; }
.rule__mark { width: 16px; text-align: center; font-weight: 700; }
.rule--on  .rule__mark { color: var(--good); }
.rule--off { color: var(--text-faint); }
.rule--off .rule__mark { color: var(--bad); }

.costs { display: inline-flex; flex-wrap: wrap; gap: 4px 8px; }
.cost { display: inline-flex; align-items: center; gap: 6px; }
.cost__plus { color: var(--text-faint); font-weight: 700; }
.price { display: inline-block; margin-right: 8px; font-weight: 600; color: var(--gold-soft); }
.price--feso { color: var(--g-element); }

/* --- shops index -------------------------------------------------------- */

.shop-region { margin-bottom: 22px; }
.shop-region__title {
  font-family: var(--font-head); font-size: 1rem; color: var(--gold-soft);
  border-bottom: 1px solid var(--border); padding-bottom: 5px; margin: 0 0 8px;
}

/* --- reference rows ----------------------------------------------------- */

.tabs { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 14px; }
.tab {
  padding: 7px 14px; font: inherit; font-size: .9rem; cursor: pointer;
  background: var(--surface-2); color: var(--text-dim);
  border: 1px solid var(--border); border-radius: 999px;
}
.tab:hover { background: var(--surface-3); text-decoration: none; }
.tab[aria-current="true"] {
  background: var(--gold); border-color: var(--gold); color: #1b1b1d; font-weight: 600;
}

.rows { display: flex; flex-direction: column; gap: 8px; }
.row {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 12px 14px;
  background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid var(--border-strong); border-radius: var(--radius);
}
.row:hover { border-left-color: var(--gold); }
.row__body { min-width: 0; flex: 1 1 auto; }
.row__name { margin: 0 0 5px; font-size: .98rem; font-family: var(--font-body); font-weight: 600; }
.row .desc { color: var(--text-dim); font-size: .9rem; overflow-wrap: anywhere; }
.row .desc span { font-weight: 600; }

.small { font-size: .82rem; }
.muted { color: var(--text-faint); }
body.no-scroll { overflow: hidden; }

@media (max-width: 680px) {
  .facts { grid-template-columns: 1fr; gap: 0 0; }
  .facts dt { margin-top: 6px; }
  .grid { font-size: .84rem; }
  .grid th, .grid td { padding: 5px 5px; }
  /* The "from which monsters" column is the first thing to go on a phone. */
  .grid td.small, .grid th:last-child:not(:nth-child(-n+2)) { display: none; }
}

/* A page with no filter sidebar (raids) gets the full width. */
.layout--wide { grid-template-columns: 1fr; }

/* The section a legacy #tab- link landed on, highlighted briefly so it is
   obvious which of a merchant's twenty tabs you asked for. */
.detail__section.is-target > h3 { color: var(--gold); border-bottom-color: var(--gold-dim); }
.detail__section.is-target { animation: target-flash 1.6s ease-out 1; }
@keyframes target-flash {
  0%   { background: color-mix(in srgb, var(--gold) 14%, transparent); }
  100% { background: transparent; }
}

/* ===========================================================================
   The in-game panels: stances, skills and item tooltips.
   ===========================================================================
   These reproduce the client's own layout, because that is the one players can
   already read at a glance. The colours are the game's, kept as their own
   tokens rather than reusing the site palette: they carry meaning (blue is a
   skill's own text, orange is its target, gold is a bonus) and they must stay
   the same in light mode, where the site's own greys all flip.
   ========================================================================= */

:root {
  --ge-gold:    #e3c265;   /* titles, column headers, bonus lines */
  --ge-gold-2:  #d9a441;   /* set effects, enchant chip */
  --ge-text:    #d7d3c8;   /* body text */
  --ge-blue:    #7fb1d6;   /* stance stat lines */
  --ge-orange:  #d79a4b;   /* older accent, kept for the item panel */

  /* The skill tooltip's own palette, sampled from the client.
     Every line in a skill card is one of these, and which one it is carries
     meaning: gold is the spec block - what the skill costs and how it behaves
     against each armour class - cream is what it does at the level you have,
     grey is what it will do at the levels past it, blue is the flavour text and
     salmon is the targeting. Getting them wrong makes the card readable but not
     skimmable - the whole point of the original layout is that you find the
     number you want by colour. */
  --sk-name:    #faf4e9;   /* the skill's name */
  --sk-spec:    #ecd070;   /* SP, casting time, cool down, duration, knockdown */
  --sk-level:   #f9e9b8;   /* "Lv.1 <skill>" and its effect lines */
  --sk-next:    #bfbfbf;   /* "Next Level" block */
  --sk-desc:    #95d7ed;   /* the description sentence */
  --sk-target:  #f38a8a;   /* range, radius, number of targets */
  --ge-dim:     #8d8778;
  --ge-frame:   #6d5b37;
  --ge-frame-2: #3b3320;   /* inner frames, one step darker than the outer */
  --ge-bg:      #100f0d;
  --ge-bg-2:    #191712;
  --ge-head:    #262218;

  /* The corner radius of every panel that wears a crest title bar.
   *
   * Not a taste decision - it is measured off titleleft.jpg. The client's own
   * corner slice is black for the first rows and the brass frame curves in
   * behind it: lit pixels start at x=9 on row 1, x=5 on row 3, x=3 on row 5 and
   * x=1 by row 8. So the artwork's corner is already round, by roughly eight
   * pixels, and a square 1px frame drawn across it reads as the corner having
   * been sliced off - which is exactly what it looked like.
   *
   * An earlier attempt at 3px made it worse, and the reason is worth keeping:
   * the radius went on the PANEL only, so the title bar kept its square corner
   * and stuck out past the rounded frame, leaving a lit notch at each end. The
   * bar and the box it sits in have to be rounded together, which is what the
   * rules below now do. Nothing is lost to the clip: the pixels inside that
   * corner are black in the artwork already. */
  --ge-radius:  8px;
}

.ge-panel {
  /* The panel sits in a drawer, a page column or a phone screen, so it sizes
     itself against its own container rather than the viewport: a media query
     here would widen the skill grid on a big monitor even when the drawer it
     lives in is 400px. */
  container-type: inline-size;
  container-name: gepanel;
  /* Rounded to the crest slice's own curve, same as .filters. */
  border: 1px solid #241f14;
  border-radius: var(--ge-radius);
  background:
    linear-gradient(180deg, #17150f 0%, var(--ge-bg) 120px, var(--ge-bg) 100%);
  color: var(--ge-text);
  font-size: .88rem;
  line-height: 1.45;
  overflow: hidden;
  margin-bottom: 14px;
}

/* The ornamental title bar, minus the ornaments. */
/* The panel title uses the same client slices as .ge-title - this is the bar
   the game draws above "Stances", "Set Effect" and every window heading. */
.ge-panel__title {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  height: 27px; padding: 0 34px;
  border-radius: var(--ge-radius) var(--ge-radius) 0 0;
  overflow: hidden;
  background: #2b2416 url('../../images/chrome/titlemid.jpg') repeat-x center;
  font-family: var(--font-head);
  font-size: .95rem; font-weight: 600;
  color: #f7ef79;
  text-shadow: 0 1px 2px #000;
}
.ge-panel__title::before,
.ge-panel__title::after {
  content: ''; position: absolute; top: 0;
  width: 30px; height: 27px;
}
.ge-panel__title::before { left: 0;  background: url('../../images/chrome/titleleft.jpg') no-repeat left top; }
.ge-panel__title::after  { right: 0; background: url('../../images/chrome/titleright.jpg') no-repeat right top; }

.ge-panel__box {
  margin: 10px;
  padding: 10px;
  border: 1px solid var(--ge-frame-2);
  border-radius: var(--radius-sm);
  background: var(--ge-bg-2);
}

/* --- columns (Information | Stats | Level 25 Bonus) ---------------------- */

.ge-cols { display: grid; gap: 8px; }
.ge-cols--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.ge-cols--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 10px; }

.ge-col {
  border: 1px solid var(--ge-frame-2);
  border-radius: var(--radius-sm);
  background: #0c0b09;
  min-width: 0;
}
.ge-col__head {
  text-align: center;
  font-family: var(--font-head);
  color: var(--ge-gold);
  font-size: .85rem;
  padding: 5px 8px;
  background: var(--ge-head);
  border-bottom: 1px solid var(--ge-frame-2);
}
.ge-col__body { padding: 9px 11px; }

.ge-lines > div { overflow-wrap: anywhere; }
.ge-lines__gap { height: .7em; }
.ge-muted { color: var(--ge-dim); }
.ge-bonus { margin: 6px 0 0; padding-left: 16px; color: var(--ge-gold); }
.ge-bonus li { margin-bottom: 3px; }

/* --- stance header ------------------------------------------------------- */

.ge-stance {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 10px 14px;
  align-items: start;
}
.ge-stance__art {
  padding: 6px;
  border: 1px solid var(--ge-frame-2);
  border-radius: var(--radius-sm);
  background: #0c0b09;
  line-height: 0;
}
.ge-stance__intro { min-width: 0; }
.ge-stance__kind {
  color: var(--ge-gold);
  font-family: var(--font-head);
  margin-bottom: 4px;
}
.ge-stance__desc { margin: 0; color: var(--ge-text); }
.ge-stance .ge-cols { grid-column: 1 / -1; }

/* The list of stances on a character page. */
.ge-stance-item { margin-bottom: 10px; }
.ge-stance-item > summary {
  cursor: pointer;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}
.ge-stance-item > summary:hover { border-color: var(--gold-dim); }
.ge-stance-item[open] > summary { border-color: var(--gold-dim); margin-bottom: 8px; }
.ge-stance-item__hands { color: var(--text-faint); font-size: .85em; }

/* --- skills -------------------------------------------------------------- */

.ge-skills {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 8px;
  align-items: start;
}
.ge-skill {
  display: flex; flex-direction: column; gap: 6px;
  min-width: 0;
}
/* A <button>, because on a narrow panel it is the accordion's handle. On a wide
   one every card is open and there is nothing to toggle, so it is reset back to
   looking and behaving like the block it used to be. */
.ge-skill__head {
  display: block; width: 100%; box-sizing: border-box;
  border: 1px solid var(--ge-frame-2);
  border-radius: var(--radius-sm);
  background: #0c0b09;
  padding: 8px 6px;
  font: inherit; color: inherit;
  text-align: center;
  position: relative;
  cursor: default;
}
.ge-skill__body { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
/* The chevron belongs to the collapsed layout and is switched on there. */
.ge-skill__caret { display: none; }
.ge-skill__no {
  position: absolute; top: 4px; left: 7px;
  color: var(--ge-gold); font-weight: 700; font-size: .78rem;
}
.ge-skill__name {
  margin-top: 5px;
  color: var(--sk-name);           /* white in the client, not gold */
  font-family: var(--font-head);
  font-size: .85rem;
  overflow-wrap: anywhere;
}
.ge-skill--empty .ge-skill__name,
.ge-skill--empty .ge-skill__no { color: var(--ge-dim); }

.ge-skill__box {
  border: 1px solid var(--ge-frame-2);
  border-radius: var(--radius-sm);
  background: #0c0b09;
  padding: 7px 9px;
  font-size: .82rem;
  line-height: 1.4;
  overflow-wrap: break-word;   /* break long words, not every word */
  hyphens: auto;
}
.ge-skill__desc   { color: var(--sk-desc); }
.ge-skill__target { color: var(--sk-target); }
.ge-skill__spec   { color: var(--sk-spec); }
.ge-skill__bp     { color: var(--sk-spec); }
/* The armour multipliers belong to the spec block and read in its gold, like
   every other line in it. */
.ge-skill__armour { color: var(--sk-spec); }

.ge-skill__level     { color: var(--sk-level); }
.ge-skill__level b   { color: var(--sk-level); display: block; margin-bottom: 2px; }
.ge-skill__leveldesc { color: inherit; }
.ge-skill__leveldesc span { font-weight: 600; }
/* Everything past the first level block is the "Next Level" half of the
   tooltip, which the client greys out so the level you actually have reads
   first. */
.ge-skill__level--next,
.ge-skill__level--next b { color: var(--sk-next); }

/* --- item tooltip -------------------------------------------------------- */

.ge-item__head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 12px;
  padding: 12px 12px 4px;
}
.ge-item__art {
  padding: 4px;
  border: 1px solid var(--ge-frame-2);
  border-radius: var(--radius-sm);
  background: #000;
  line-height: 0;
  align-self: start;
}
.ge-item__intro { min-width: 0; }
.ge-item__name {
  color: var(--ge-gold);
  font-family: var(--font-head);
  font-size: 1rem;
  margin-bottom: 3px;
}
.ge-item__req  { color: var(--ge-blue); }
.ge-item__desc { color: var(--ge-text); margin-top: 2px; }
.ge-item__desc span { font-weight: 600; }
.ge-item__set {
  margin-top: 5px; padding: 0;
  background: none; border: 0; font: inherit; cursor: pointer;
  color: var(--ge-gold-2); text-align: left;
}
.ge-item__set:hover { text-decoration: underline; }
.ge-chip-line { color: var(--ge-gold-2); margin-top: 4px; }

.ge-setrow { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 0 0; }
.ge-setrow__piece {
  font-size: .82rem; padding: 3px 9px; border-radius: 999px;
  border: 1px solid var(--ge-frame-2); color: var(--ge-text);
}
.ge-setrow__piece[aria-current="true"] { border-color: var(--ge-gold); color: var(--ge-gold); }

/* --- recipe -------------------------------------------------------------- */

.ge-recipe { margin: 10px; border: 1px solid var(--ge-frame-2); border-radius: var(--radius-sm); overflow: hidden; }
.ge-recipe__head {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 10px;
  background: var(--ge-head);
  color: var(--ge-gold);
  font-family: var(--font-head);
  border-bottom: 1px solid var(--ge-frame-2);
}
.ge-recipe__table { width: 100%; border-collapse: collapse; background: #0c0b09; }
.ge-recipe__table th {
  text-align: left; padding: 5px 10px;
  font-size: .72rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--ge-dim); font-weight: 700;
  border-bottom: 1px solid var(--ge-frame-2);
}
.ge-recipe__table th:last-child, .ge-recipe__table td:last-child { text-align: right; width: 1%; white-space: nowrap; }
.ge-recipe__table td { padding: 5px 10px; border-bottom: 1px solid #1c1a15; }
.ge-recipe__table tr:last-child td { border-bottom: 0; }
.ge-recipe__table a { color: var(--ge-text); }
.ge-recipe__table a:hover { color: var(--ge-gold); }
.ge-ing { display: inline-flex; align-items: center; gap: 8px; }
.ge-num { font-variant-numeric: tabular-nums; color: var(--ge-text); }

/* --- not in this version ------------------------------------------------- */
/* Shown in a detail panel, and server-side in the page body, when an address
   from another edition names something this database does not have. */

/* The 404 page. Centred like .nothere below it, and saying the same thing one
   level up: that page was not reached at all, rather than reached and empty. */
.notfound {
  max-width: 44rem; margin: 48px auto; padding: 0 16px;
  text-align: center;
}
.notfound h1 {
  margin: 0 0 14px;
  color: var(--gold-soft, #e3c265);
  font-family: var(--font-head, Georgia, serif);
}
.notfound__lead { margin: 0 0 12px; color: var(--text); font-size: 1.02rem; line-height: 1.6; }
/* The address that failed, quoted back. Small, because it is evidence rather
   than instruction - but there, because "that page" is not an answer when you
   have three editions open in three tabs. */
.notfound__asked { margin: 0 0 18px; }
.notfound__asked code {
  padding: 3px 8px;
  background: var(--surface-2, #1b1812);
  border: 1px solid var(--border, #3a372f); border-radius: var(--radius-sm, 4px);
  color: var(--text-faint); font-size: .84rem; overflow-wrap: anywhere;
}
.notfound__alt { margin: 0 0 20px; color: var(--text-dim, #b9b3a5); }
.notfound__ways {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 10px;
  margin: 0; padding: 0; list-style: none;
}
.notfound__ways li { margin: 0; }
.notfound__ways a {
  display: inline-block; padding: 7px 14px;
  background: var(--surface-2, #1b1812);
  border: 1px solid var(--border, #3a372f); border-radius: 999px;
  font-size: .9rem;
}
.notfound__ways a:hover { border-color: var(--gold-dim, #a8880c); text-decoration: none; }

.nothere { text-align: center; }
.nothere__lead { margin: 0 0 6px; color: var(--text); font-weight: 600; }
.nothere__alt { margin: 0; color: var(--text-dim); font-size: .9rem; }
.nothere__alt a { color: var(--ge-gold); }

/* --- where to get it ----------------------------------------------------- */
/* Framed like the recipe block above, because it answers the same kind of
   question and sits directly under it: one list per kind of source, each with
   its own small heading and an optional link out to the page that holds all of
   them. */

.ge-sources { margin: 10px; border: 1px solid var(--ge-frame-2); border-radius: var(--radius-sm); overflow: hidden; }
.ge-sources__head {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 10px;
  background: var(--ge-head);
  color: var(--ge-gold);
  font-family: var(--font-head);
  border-bottom: 1px solid var(--ge-frame-2);
}
.ge-sources__body { background: #0c0b09; }
.ge-sources__body > .ge-muted { padding: 9px 11px; }

.ge-src + .ge-src { border-top: 1px solid var(--ge-frame-2); }
.ge-src__head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  padding: 8px 10px 2px;
  font-family: var(--font-head); font-size: .82rem; color: var(--ge-gold);
}
.ge-src__more { font-size: .75rem; color: var(--ge-dim); white-space: nowrap; }
.ge-src__more:hover { color: var(--ge-gold); }
/* "+N more" is a button when it reveals the hidden rows rather than linking
   somewhere; it has to stop looking like a form control to sit in a heading. */
button.ge-src__more {
  background: none; border: 0; padding: 0; font: inherit; font-size: .75rem;
  cursor: pointer; text-decoration: underline; text-decoration-style: dotted;
  text-underline-offset: 2px;
}

.ge-src__table { width: 100%; border-collapse: collapse; }
.ge-src__table th {
  text-align: left; padding: 4px 10px;
  font-size: .72rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--ge-dim); font-weight: 700;
  border-bottom: 1px solid var(--ge-frame-2);
}
.ge-src__table td { padding: 5px 10px; border-bottom: 1px solid #1c1a15; vertical-align: top; }
.ge-src__table tr:last-child td { border-bottom: 0; }
.ge-src__table td:not(:first-child) { font-variant-numeric: tabular-nums; white-space: nowrap; }
.ge-src__table a { color: var(--ge-text); }
.ge-src__table a:hover { color: var(--ge-gold); }
.ge-src__sub { display: block; font-size: .78rem; color: var(--ge-dim); }
.ge-src .tag { margin-left: 6px; }

/* Links inside the panel keep the body colour, which is the panel's house
   style and also made them impossible to find: in the acquisition block the
   whole first column is links and not one of them looked like one, so the
   obvious conclusion was that the names were dead text. A dotted underline
   says "this opens something" without turning the panel into a page of gold;
   the gold still arrives on hover, as it always did. */
.ge-src__table a,
.ge-recipe__table a,
.ge-eq a {
  text-decoration: underline;
  text-decoration-style: dotted;
  text-decoration-color: var(--ge-dim);
  text-underline-offset: 2px;
}
.ge-src__table a:hover,
.ge-recipe__table a:hover,
.ge-eq a:hover { text-decoration-color: currentColor; }

/* On a phone the drop table's fourth column - the quantity, which is 1 almost
   everywhere - is what gets squeezed, so it goes rather than the chance. */
@media (max-width: 560px) {
  .ge-src__table th:nth-child(4), .ge-src__table td:nth-child(4) { display: none; }
  .ge-src__table td:not(:first-child) { white-space: normal; }
}

/* --- sprites ------------------------------------------------------------- */

/* The sheets keep their native pixel size; the box scales the sprite into the
   space the layout wants, so one sheet serves 24px and 64px icons alike. */
.ge-sprite { display: inline-block; position: relative; overflow: hidden; flex: none; }
.ge-sprite > i {
  position: absolute; top: 0; left: 0;
  transform-origin: top left;
  background-repeat: no-repeat;
  image-rendering: auto;
}
.ge-sprite--blank { background: #0c0b09; border-radius: 2px; }

/* --- a flat stat filter list needs a taller scroller --------------------- */
.facet__list--tall { max-height: 420px; padding: 0; }
.facet__search {
  width: calc(100% - 20px); margin: 6px 10px; padding: 5px 8px;
  font: inherit; font-size: .85rem;
  background: var(--ge-bg, var(--surface-2)); color: var(--ge-text, var(--text));
  border: 1px solid var(--ge-frame-2, var(--border)); border-radius: 2px;
}

/* --- Basic Character Information ----------------------------------------- */

/* The portrait sits left with the six primary stats in two columns beside it,
   which is how the game's own character window reads: STR/HP/INT down one side,
   AGI/DEX/SEN down the other. */
.ge-char__head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 14px;
  align-items: start;
  padding: 12px;
}
/* Rounded, and clipping what it holds.
   The barrack bitmaps are chroma-keyed on magenta and the key does not quite
   reach the last pixel of each corner - a few stray pink dots survive the key,
   which are invisible in the game because its own frame covers them. Rounding
   the frame by the same few pixels covers them here too, and costs nothing on
   the portraits that have no such edge. */
.ge-char__portrait {
  border: 1px solid var(--ge-frame);
  border-radius: 6px;
  background: #0c0b09;
  padding: 3px;
  line-height: 0;
  overflow: hidden;
}
/* Native size: 63x100 is what the client draws, and scaling it up only softens
   it. `block` so the line box does not add a descender gap under it. */
.ge-char__barrack {
  display: block;
  width: 63px; height: 100px;
  border-radius: 4px;
}
.ge-char__ident { min-width: 0; }
.ge-char__name {
  font-family: var(--font-head);
  font-size: 1.15rem; color: var(--ge-gold);
  margin-bottom: 8px;
}

.ge-char__ring {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2px 14px; margin-bottom: 10px;
}
.ge-char__side { display: flex; flex-direction: column; gap: 2px; }
.ge-char__stat {
  display: flex; align-items: baseline; gap: 8px;
  border-bottom: 1px solid var(--ge-frame-2);
  padding: 2px 0;
}
.ge-char__statname {
  color: var(--ge-gold); font-size: .78rem;
  letter-spacing: .06em; min-width: 3.2em;
}
.ge-char__statval { margin-left: auto; color: var(--ge-text); font-variant-numeric: tabular-nums; }

.ge-char__facts { color: var(--ge-blue); font-size: .9rem; }
.ge-char__key { color: var(--ge-dim); }

/* Biography on the left, starting gear and the buff on the right. */
.ge-char__story {
  display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  gap: 0;
  padding: 0;
}
.ge-char__story > * + * { border-left: 1px solid var(--ge-frame-2); }
.ge-char__story > .ge-col { border: 0; }
.ge-char__bio { color: var(--ge-text); margin: 0; }

.ge-char__gear { display: flex; flex-direction: column; gap: 6px; padding: 8px 11px; }
.ge-char__gearrow { display: flex; align-items: center; gap: 8px; }
.ge-char__gearslot { color: var(--ge-dim); font-size: .74rem; text-transform: uppercase; letter-spacing: .05em; }
.ge-char__item { color: var(--ge-text); }
.ge-char__item:hover { color: var(--ge-gold); }
.ge-char__buff { padding: 8px 11px; color: var(--ge-gold-2); }
.ge-char__bufftip { color: var(--ge-blue); margin-top: 2px; font-size: .9em; }

/* The job skill reuses the stance skill card, but it is one wide card rather
   than one of five columns, so the head runs along a row - icon, name, label -
   instead of stacking centred. */
.ge-char__job .ge-skill__head {
  display: flex; align-items: center; gap: 10px;
  text-align: left; padding: 8px 10px;
}
.ge-char__job .ge-skill__name { margin-top: 0; font-size: 1rem; }
.ge-char__job .ge-skill__no {
  position: static; margin-left: auto;
  color: var(--ge-dim); font-weight: 400; text-transform: uppercase;
  letter-spacing: .05em; white-space: nowrap;
}

/* Category | Usable Equipment */
.ge-eqtable { width: 100%; border-collapse: collapse; background: #0c0b09; }
.ge-eqtable th {
  text-align: left; padding: 5px 10px;
  background: var(--ge-head); color: var(--ge-gold);
  font-size: .74rem; text-transform: uppercase; letter-spacing: .05em;
  font-weight: 600;
}
.ge-eqtable tbody th {
  background: transparent; color: var(--ge-dim);
  white-space: nowrap; vertical-align: top; width: 1%;
  text-transform: none; letter-spacing: 0; font-size: .85rem;
}
.ge-eqtable td { padding: 5px 10px; border-bottom: 1px solid #1c1a15; }
.ge-eqtable tbody th { border-bottom: 1px solid #1c1a15; }
.ge-eqtable tr:last-child td, .ge-eqtable tr:last-child th { border-bottom: 0; }

.ge-eq {
  display: inline-flex; align-items: baseline; gap: 5px;
  margin: 0 6px 3px 0; padding: 1px 7px;
  border: 1px solid var(--ge-frame-2); border-radius: 2px;
  font-size: .85rem;
}
.ge-eq a { color: var(--ge-text); }
.ge-eq a:hover { color: var(--ge-gold); }
.ge-eq em { font-style: normal; font-size: .8em; color: var(--ge-dim); }
.ge-eq__sep { color: var(--ge-frame); }
/* Gear no other character can use: the point of the table.
   Marked by the text colour alone. A gold border round the whole group read as
   a box drawn round the answer rather than as emphasis on it, and the rows it
   lands on - Head and Costume - are the ones with the longest values, so the
   box was also the widest thing in the table. */
.ge-eq--only { border-color: transparent; padding-left: 0; }
.ge-eq--only a, .ge-eq--only > span { color: var(--ge-gold-2); }

/* --- the Stances table --------------------------------------------------- */

/* Equipped Items | icons | Available Stances, the way the game indexes them.
   The open stance's panel sits UNDER the whole table rather than inside a row,
   so opening one never moves the row you clicked. */
.ge-stances { margin-top: 16px; }
.ge-stancetable {
  width: 100%;
  /* `separate` with zero spacing rather than `collapse`, purely so the two
     bottom corners can be rounded: in the collapsed border model a table's
     border-radius is ignored, and this table is the bottom half of the box
     whose top half is the crest bar above it. Nothing doubles up, because
     every rule below sets one side of a cell, never a full box. */
  border-collapse: separate; border-spacing: 0;
  background: #0c0b09;
  border: 1px solid var(--ge-frame-2);
  border-radius: 0 0 var(--ge-radius) var(--ge-radius);
}
.ge-stancetable thead th {
  padding: 5px 10px; text-align: center;
  background: var(--ge-head); color: var(--ge-gold);
  font-size: .74rem; text-transform: uppercase; letter-spacing: .05em;
  border-bottom: 1px solid var(--ge-frame-2);
}
.ge-stancetable td, .ge-stancetable tbody th {
  padding: 4px 10px; border-bottom: 1px solid #1c1a15;
  vertical-align: middle;
}
.ge-stancetable tr:last-child td, .ge-stancetable tr:last-child th { border-bottom: 0; }

.ge-stances__hands {
  text-align: left; white-space: nowrap; width: 1%;
  color: var(--ge-gold-2); font-weight: 400; font-size: .88rem;
  border-right: 1px solid #1c1a15;
}
.ge-stances__icons {
  width: 1%; white-space: nowrap;
  border-right: 1px solid #1c1a15;
  text-align: center;
}
.ge-stances__icon {
  background: none; border: 1px solid transparent; border-radius: 2px;
  padding: 1px; margin: 1px; cursor: pointer; line-height: 0;
}
.ge-stances__icon:hover { border-color: var(--ge-gold-2); }
.ge-stances__icon[aria-expanded="true"] { border-color: var(--ge-gold); background: rgb(227 194 101 / .14); }

.ge-stances__names { color: var(--ge-dim); }
.ge-stances__name {
  background: none; border: 0; padding: 1px 0; cursor: pointer;
  font: inherit; font-size: .88rem; color: var(--ge-gold-2);
}
.ge-stances__name:hover { color: var(--ge-gold); text-decoration: underline; }
.ge-stances__name[aria-expanded="true"] { color: var(--ge-gold); font-weight: 600; }
.ge-stances__sep { color: var(--ge-dim); }

.ge-stances__panel { margin-top: 10px; }
.ge-stances__panel[hidden] { display: none; }

/* --- the stance table on a phone -----------------------------------------
   Three columns do not fit in a 350px drawer. "Rod" and the icon strip each
   claim a fixed width, the stance names get whatever is left - which is not
   enough for "Telekinesis (Lv 100)" - and the table pushes a horizontal
   scrollbar under the whole panel.

   So below 560px it stops being a table and becomes a list: the header row and
   the icon column go, and the weapon stays as a one-line heading above its own
   stances rather than as a column beside them. It costs no width there and it
   is the only place the pairing is recorded - the panel that opens is titled
   with the stance's name alone, so dropping it outright would leave no way to
   tell which weapon a stance belongs to.

   The names stack instead of running together comma-separated: one per line is
   both easier to read and a tap target a thumb can hit. */
@media (max-width: 560px) {
  .ge-stancetable, .ge-stancetable tbody, .ge-stancetable tr { display: block; }
  .ge-stancetable thead { display: none; }
  .ge-stances__icons { display: none; }

  .ge-stancetable tr { border-bottom: 1px solid #1c1a15; }
  .ge-stancetable tr:last-child { border-bottom: 0; }
  .ge-stancetable td, .ge-stancetable tbody th { border-bottom: 0; }

  .ge-stances__hands {
    display: block; width: auto; border-right: 0;
    padding: 7px 10px 2px;
    font-size: .76rem; text-transform: uppercase; letter-spacing: .05em;
    color: var(--ge-gold);
  }
  .ge-stances__names { display: block; padding: 0 10px 7px; }
  .ge-stances__sep { display: none; }
  .ge-stances__name { display: block; width: 100%; text-align: left; padding: 5px 0; }
}

/* A skill icon that is its own file rather than a rect on a sheet. */
/* inline-block, not block: the card head centres its contents with text-align,
   which a block-level image ignores - it sat hard against the left edge while
   the skill name under it was centred. */
.ge-skillicon { display: inline-block; object-fit: contain; image-rendering: auto; }

/* --- narrow screens ------------------------------------------------------ */

/* Five columns is the game's own layout and it holds down to ~860px, which is
   about what the original page used. Below that the cards would be ribbons, so
   they step down rather than shrink. */
@container gepanel (max-width: 860px) {
  .ge-skills { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .ge-cols--3 { grid-template-columns: 1fr; }
}
/* --- the phone layout: one column, one skill open ------------------------
   Below this the five cards are stacked, and stacked they are enormous: a skill
   is a description, a targeting line, thirteen spec lines and up to seven level
   blocks, so reaching the fifth skill of a stance meant scrolling past four of
   those. The original site collapsed them for exactly this reason.
   So each head becomes a row you tap, one body is open at a time (see
   wireSkillAccordion in gepanel.js), and the whole stance fits on a screen.
   Two columns would fight the accordion - one open card beside a collapsed one
   leaves a tall ragged hole - so this is one column from here down. */
@container gepanel (max-width: 620px) {
  .ge-skills { grid-template-columns: 1fr; gap: 6px; }
  .ge-char__story { grid-template-columns: 1fr; }
  .ge-char__story > * + * { border-left: 0; border-top: 1px solid var(--ge-frame-2); }

  .ge-skill__head {
    display: flex; align-items: center; gap: 10px;
    text-align: left; padding: 6px 10px 6px 26px;
    cursor: pointer;
  }
  .ge-skill--empty .ge-skill__head { cursor: default; }
  .ge-skill__no { top: 50%; left: 8px; transform: translateY(-50%); }
  .ge-skill__name { margin-top: 0; flex: 1 1 auto; min-width: 0; }

  .ge-skill__caret {
    display: block; flex: none;
    width: 0; height: 0;
    border: 5px solid transparent; border-top-color: var(--ge-gold);
    margin-top: 3px;
    transition: transform .15s ease;
  }
  .ge-skill[data-open="true"] .ge-skill__caret { transform: rotate(180deg); margin-top: -3px; }

  .ge-skill[data-open="false"] .ge-skill__body { display: none; }
  .ge-skill[data-open="true"] > .ge-skill__head { border-color: var(--ge-frame); }
}
@container gepanel (max-width: 430px) {
  .ge-skills { grid-template-columns: 1fr; }
  .ge-cols--2 { grid-template-columns: 1fr; }
  .ge-stance { grid-template-columns: 1fr; }
  .ge-stance__art { justify-self: start; }
  .ge-char__head { grid-template-columns: 1fr; }
  .ge-char__ring { grid-template-columns: 1fr; }
}
/* Browsers without container queries still get a readable page. */
@supports not (container-type: inline-size) {
  .ge-skills { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ge-cols--3, .ge-cols--2 { grid-template-columns: 1fr; }
  .ge-char__story { grid-template-columns: 1fr; }
  .ge-char__story > * + * { border-left: 0; border-top: 1px solid var(--ge-frame-2); }
}

/* Set bonuses, one block per "wear N pieces" tier. */
.ge-settiers { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1px; background: var(--ge-frame-2); }
.ge-settier { background: #0c0b09; padding: 8px 10px; }
.ge-settier__n {
  color: var(--ge-gold); font-size: .74rem; text-transform: uppercase;
  letter-spacing: .05em; margin-bottom: 4px;
}

/* ============================================================= portal ====
   home.php, news.php and search.php: the front page's search panel and the
   news list, rebuilt on this site's chrome rather than the old stylesheet's.
   Both panels are the game's window - a gold title bar over a dark frame - so
   the portal reads as the same product as the pages it leads to.
   ======================================================================= */

.home {
  display: flex; flex-direction: column; align-items: center;
  gap: 22px;
  padding: 26px var(--gutter) 48px;
}
.home > * { width: 100%; max-width: 760px; }

.home__search, .home__news, .search__group {
  border: 1px solid #241f14;
  background: var(--ge-bg, var(--surface));
  /* Same corner as the crest bar that tops each of these, and overflow so the
     news table's first row is clipped to it rather than squaring it off again. */
  border-radius: var(--ge-radius);
  overflow: hidden;
}
.home__search { max-width: 460px; }
.home--search .home__search { margin-bottom: 4px; }

/* The search row. `position: relative` is the anchor for the suggestion list. */
.home__form {
  position: relative;
  display: flex; flex-wrap: wrap; gap: 8px;
  padding: 16px;
}
.home__form select { flex: 1 1 130px; min-width: 0; }
.home__form input[type="search"] { flex: 999 1 190px; min-width: 0; }
.home__form .btn { flex: 1 1 100%; justify-content: center; }
/* Brass, not a slab of gold: the original portal's Search was a full-width bar
   in the frame's own metal with gold type, and a solid gold rectangle that size
   outshouts the news panel it sits above. */
.btn--gold {
  background: linear-gradient(to bottom, rgb(52 54 44 / .92), rgb(31 28 20 / .96));
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .14);
  border-color: var(--ge-frame, #6d5b37);
  color: #ffcc00; font-weight: 600; letter-spacing: .02em;
}
.btn--gold:hover {
  background-image: none; background-color: rgb(0 0 0 / .5);
  border-color: var(--ge-gold, var(--gold));
}
.btn--gold .ge-ico { width: 14px; height: 14px; }

/* Suggestions hang under the box; the shared dropdown styling, re-anchored to
   the form rather than to the header's narrow search field. */
.home__suggest { top: calc(100% - 8px); left: 16px; right: 16px; }

/* The row of section links under the box: "Characters 296". The counts are the
   reason it earns its space, the same way the nav bar's do - they say at a
   glance that the database is current. */
.home__counts {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 4px 6px;
  margin: 0; padding: 0 16px 16px;
  font-size: .82rem;
}
.home__counts a {
  padding: 3px 9px; border-radius: 999px;
  border: 1px solid var(--ge-frame-2, var(--border));
  color: var(--text-dim);
}
.home__counts a:hover { border-color: var(--ge-gold, var(--gold)); color: var(--ge-gold, var(--gold)); text-decoration: none; }
.home__counts b { color: var(--ge-gold, var(--gold)); font-variant-numeric: tabular-nums; }

/* The flourish between the two panels. The old portal had a drawn one; this is
   the same gesture in two rules, with a diamond where the strokes meet. */
.home__rule {
  height: 1px; max-width: 420px;
  background: linear-gradient(to right, transparent, var(--ge-frame, #6d5b37), transparent);
  position: relative;
}
.home__rule::after {
  content: ''; position: absolute; left: 50%; top: 50%;
  width: 7px; height: 7px; margin: -4px 0 0 -4px;
  background: var(--ge-gold, var(--gold)); transform: rotate(45deg);
}

/* --- the news table ----------------------------------------------------- */

.newstable { width: 100%; border-collapse: collapse; font-size: .9rem; }
.newstable thead th {
  text-align: left; padding: 7px 12px;
  background: var(--ge-head, var(--surface-2));
  color: var(--ge-gold, var(--gold));
  font-family: var(--font-head); font-size: .82rem;
  letter-spacing: .04em; text-transform: uppercase;
  border-bottom: 1px solid #000;
}
.newstable td { padding: 9px 12px; border-top: 1px solid var(--ge-frame-2, var(--border)); vertical-align: top; }
.newstable tbody tr:nth-child(even) { background: rgb(255 255 255 / .022); }
/* The date is one unbreakable column so a three-word message never squeezes
   "Jul 28," onto its own line, and it is gold because it is the thing you scan
   this table by. */
.newstable__date {
  white-space: nowrap; width: 1%;
  color: var(--ge-gold-2, var(--gold-soft)); font-weight: 600;
}
.newstable__body { color: var(--text); line-height: 1.5; }
.newstable__body ul { margin: 0; padding-left: 18px; }
.newstable__body li { margin: 2px 0; }
.newstable__body a { color: var(--ge-gold-2, var(--gold-soft)); }

.home__more { text-align: center; margin: 0; padding: 12px; }
.home__more a { color: var(--ge-gold-2, var(--gold-soft)); }

/* --- all-sections search results ---------------------------------------- */

.home--search #results { width: 100%; max-width: 760px; }
.search__count { color: var(--text-dim); text-align: center; margin: 0 0 16px; }
.search__group { margin-bottom: 14px; }
.search__list { list-style: none; margin: 0; padding: 4px 0; }
.search__list li {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px;
  padding: 5px 14px;
}
.search__list li + li { border-top: 1px solid var(--ge-frame-2, var(--border)); }
.search__list small { color: var(--text-faint); font-size: .8rem; }

@media (max-width: 560px) {
  .home__form { padding: 12px; }
  .home__form select, .home__form input[type="search"] { flex-basis: 100%; }
  .home__suggest { left: 12px; right: 12px; }
  .newstable { font-size: .86rem; }
  .newstable td { padding: 8px 9px; }
}

/* ======================================================== back to top ====
   The old site's button, in the same place: bottom left, a gold tile with a
   chevron. Bottom LEFT rather than the usual right because that is where it
   was, and because the right-hand edge of this site is where the detail drawer
   and the filter sheet come from.

   It appears only once there is something to go back up from, and it sits below
   the drawer and its scrim (z-index 70 and 55), so opening one covers it rather
   than leaving a button floating over the dimmed page.
   ======================================================================= */

.to-top {
  position: fixed; z-index: 50;
  left: 14px; bottom: 14px;
  width: 46px; height: 46px;
  display: grid; place-items: center;
  padding: 0; cursor: pointer;
  background: var(--gold); color: #1b1b1d;
  border: 1px solid rgb(0 0 0 / .45);
  border-radius: 9px;
  box-shadow: 0 3px 10px rgb(0 0 0 / .55);
  /* Hidden state: still laid out, so showing it costs no reflow, and it fades
     and lifts rather than appearing under the pointer. */
  opacity: 0; transform: translateY(10px); pointer-events: none;
  transition: opacity .18s ease, transform .18s ease, background-color .18s ease;
}
.to-top[data-show="true"] { opacity: 1; transform: none; pointer-events: auto; }
.to-top:hover { background: var(--gold-soft); }
.to-top svg { width: 22px; height: 22px; fill: currentColor; display: block; }

@media (max-width: 560px) {
  .to-top { left: 10px; bottom: 10px; width: 42px; height: 42px; }
}
/* Someone who has asked for less motion gets the button, not the animation. */
@media (prefers-reduced-motion: reduce) {
  .to-top { transition: none; transform: none; }
}

/* The server-rendered summary a deep link lands on, before the detail drawer
   opens over it. Short, and styled as a lede rather than as a panel: it is the
   page's own answer to "what is this", not a second copy of the drawer. */
.lede {
  margin: 0 0 14px;
  padding: 12px 14px;
  background: var(--ge-bg-2, var(--surface));
  border: 1px solid var(--ge-frame-2, var(--border));
  border-left: 3px solid var(--ge-gold, var(--gold));
  border-radius: var(--radius-sm);
  color: var(--text-dim); font-size: .92rem; line-height: 1.55;
}
.lede p { margin: 0 0 6px; }
.lede p:last-child { margin-bottom: 0; }
.lede strong { color: var(--ge-gold, var(--gold)); }
.lede__stats { color: var(--text); font-variant-numeric: tabular-nums; }
.lede__stats b { color: var(--ge-gold-2, var(--gold-soft)); }
.lede__bio { color: var(--text-dim); }

/* ====================================================== edition switcher ==
   The stack of game versions at the top left of every page, over the banner.
   Absolutely positioned, as the old site had it: it must not take a row of its
   own, because on a phone the banner is already most of the first screen.

   The current edition is a <span>, not a link, and it is the one in plain
   white: the others are gold because gold means "you can go there" everywhere
   else on this site, and a highlighted link to the page you are already on is
   the oldest navigation mistake there is.
   ======================================================================= */

.site-ver {
  position: absolute; top: 0; left: 0; z-index: 3;
  padding: 5px 9px;
  font-size: .8rem; line-height: 1.5;
  text-shadow: 0 1px 3px #000, 0 0 8px #000;
}
.site-ver a, .site-ver span { display: block; }
.site-ver a { color: var(--gold-soft, #e3c265); text-decoration: none; }
.site-ver a:hover { color: var(--gold, #ffd76a); text-decoration: underline; }
.site-ver [aria-current="true"] { color: #fff; font-weight: 600; }

@media (max-width: 560px) {
  .site-ver { font-size: .74rem; padding: 3px 7px; }
}

/* ========================================================= version picker ==
   The site root: which of the databases do you want. Nothing else on it, on
   purpose - it is a fork in the road, and the old site got that right.
   ======================================================================= */

.picker { display: flex; justify-content: center; padding: 20px var(--gutter) 60px; }
.picker__panel { width: 100%; max-width: 420px; }
.picker__list { display: block; }
.picker__row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px;
  border-top: 1px solid var(--ge-frame-2, var(--border));
  color: var(--text); text-decoration: none;
}
.picker__row:first-child { border-top: 0; }
.picker__row:hover { background: rgb(255 255 255 / .04); }
.picker__row img {
  width: 120px; height: 61px; flex: none;
  object-fit: contain;
  border: 1px solid #45423a;
  background-color: #25282d;
}
.picker__name {
  display: block;
  font-family: var(--font-head); font-size: 1.05rem;
  color: var(--ge-gold-2, var(--gold-soft));
}
.picker__row:hover .picker__name { color: var(--gold, #ffd76a); }
.picker__when { display: block; font-size: .78rem; color: var(--text-faint); margin-top: 2px; }
/* An edition that is announced but not open yet: listed, not a link, and
   visibly quieter than the ones you can click. */
.picker__row--soon { cursor: default; }
.picker__row--soon:hover { background: none; }
.picker__row--soon .picker__name,
.picker__row--soon:hover .picker__name { color: var(--text-dim); }
.picker__row--soon .picker__when { font-style: italic; }

@media (max-width: 420px) {
  .picker__row img { width: 88px; height: 45px; }
}


/* ========================================================= launch countdown ==
   The clock to the Andromida Rising server opening, on the version picker and
   across that edition's masthead. The database is open before the server is, so
   this is the one thing a visitor most wants to know and the page cannot show
   any other way.

   The numbers are monospaced-by-metric (tabular-nums) and the boxes have a
   floor width, so a second ticking from 9 to 10 does not shove the row sideways
   sixty times a minute. */

.cd {
  --cd-accent: var(--ge-gold, var(--gold, #ffcc00));
  cursor: pointer;
  user-select: none;
}
.cd__lead {
  display: flex; align-items: center; gap: 6px;
  margin: 0 0 5px;
  font-size: .74rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-dim);
}
.cd__dot {
  width: 7px; height: 7px; flex: none; border-radius: 50%;
  background: var(--cd-accent);
  box-shadow: 0 0 0 0 rgb(255 204 0 / .55);
  animation: cd-pulse 2.4s ease-out infinite;
}
@keyframes cd-pulse {
  70%  { box-shadow: 0 0 0 7px rgb(255 204 0 / 0); }
  100% { box-shadow: 0 0 0 0 rgb(255 204 0 / 0); }
}

.cd__units { display: flex; gap: 6px; margin: 0; }
.cd__unit {
  min-width: 46px; padding: 5px 6px 4px;
  text-align: center;
  background: var(--surface-2, #2b2b2f);
  border: 1px solid var(--ge-frame-2, var(--border, #3d3d44));
  border-radius: var(--radius-sm, 5px);
}
.cd__unit b {
  display: block;
  font-family: var(--font-head, Georgia, serif);
  font-size: 1.2rem; line-height: 1.1; font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--cd-accent);
}
.cd__unit i {
  display: block; font-style: normal;
  font-size: .6rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-faint);
}

.cd__bar {
  height: 3px; margin: 7px 0 0;
  background: var(--surface-3, #34343a);
  border-radius: 2px; overflow: hidden;
}
.cd__bar i {
  display: block; height: 100%;
  background: linear-gradient(90deg, var(--gold-dim, #a8880c), var(--cd-accent));
  transition: width .9s linear;
}

.cd__local {
  margin: 0;
  font-family: var(--font-head, Georgia, serif);
  font-size: 1rem; color: var(--cd-accent);
}
.cd__hint {
  margin: 5px 0 0;
  font-size: .68rem; color: var(--text-faint);
}
.cd:hover .cd__hint { color: var(--text-dim); }

/* Clicked: the remaining time gives way to the moment itself, in the reader's
   own timezone - the question every countdown raises. */
.cd[data-mode="when"] .cd__units { display: none; }
.cd[data-mode="when"] .cd__local { display: block; }

/* Zero. The lead line says so; the boxes would only read 0 0 00 00. */
.cd.is-live .cd__units,
.cd.is-live .cd__bar,
.cd.is-live .cd__hint { display: none; }
.cd.is-live .cd__lead { color: var(--cd-accent); font-size: .95rem; text-transform: none; letter-spacing: 0; }

/* --- on the version picker, under the edition's name --------------------- */
.cd--full { margin-top: 7px; }

/* --- across a site header, above the brass bar --------------------------- */
.cd--strip {
  display: flex; align-items: center; justify-content: center;
  flex-wrap: wrap; gap: 4px 14px;
  padding: 7px 14px;
  background: linear-gradient(to bottom, rgb(12 11 9 / .55), rgb(12 11 9 / .85));
  border-top: 1px solid rgb(255 204 0 / .18);
  text-shadow: 0 1px 3px #000;
}
.cd--strip .cd__lead { margin: 0; }
.cd--strip .cd__unit { min-width: 42px; padding: 3px 6px 2px; background: rgb(0 0 0 / .35); }
.cd--strip .cd__unit b { font-size: 1rem; }
.cd--strip .cd__bar { display: none; }
.cd--strip .cd__hint { margin: 0; }
.cd--strip .cd__local { font-size: .9rem; }

@media (max-width: 560px) {
  .cd--strip { gap: 4px 10px; padding: 6px 10px; }
  .cd--strip .cd__hint { display: none; }
  .cd__unit { min-width: 40px; }
}

@media (prefers-reduced-motion: reduce) {
  .cd__dot { animation: none; }
  .cd__bar i { transition: none; }
}

/* ====================================================== status references ==
   [Deep Sleep] inside a skill description: what the game makes a link, and what
   this site rendered as three plain words for no reason. Gold like every other
   link here, with a dotted underline rather than a solid one so a sentence with
   four of them does not turn into a row of buttons.
   ======================================================================= */

.statusref {
  color: var(--ge-gold-2, var(--gold-soft));
  text-decoration: underline dotted;
  text-underline-offset: 2px;
  cursor: help;
}
.statusref:hover, .statusref:focus-visible { color: var(--gold, #ffd76a); text-decoration-style: solid; }

/* The hover card. Fixed and on <body> because the panels it appears in are
   overflow: hidden - drawn in the flow it would be cut off at the panel edge,
   which is exactly where a skill card's last line is. ui.js positions it. */
.statustip {
  position: fixed; z-index: 90;
  max-width: min(320px, calc(100vw - 16px));
  padding: 8px 10px;
  border: 1px solid var(--ge-frame, #6d5b37);
  border-radius: var(--radius-sm);
  background: #16140f;
  box-shadow: 0 6px 20px rgb(0 0 0 / .6);
  color: var(--ge-text, var(--text));
  font-size: .82rem; line-height: 1.5;
  white-space: pre-line;          /* the client writes these with <br> breaks */
  pointer-events: none;
  opacity: 0; transition: opacity .12s ease;
}
.statustip[data-show="true"] { opacity: 1; }

/* The row a status link landed on, flashed the way a legacy #tab- link is. */
.row.is-target { animation: target-flash 1.6s ease-out 1; }

/* ============================================================== footer ====
   Two paragraphs: what this site is, and whose trademarks it is about. The
   rights notice is small and dim on purpose - it has to be there, it is not
   what anyone came for, and the old site set it the same way.
   ======================================================================= */

/* Centred, like the original's. The rights notice keeps its measure - 96ch of
   trademark text set flush across a 1400px page is unreadable - but the box
   itself is centred with `margin-inline: auto` rather than sitting at the left
   edge, which is what `text-align` alone would have left it doing. */
.site-footer { text-align: center; }
.site-footer__meta { margin: 0 0 10px; }
.site-footer__legal {
  margin: 0 auto;
  max-width: 96ch;
  font-size: .74rem; line-height: 1.7;
  color: var(--text-faint);
}
.site-footer__legal a { color: var(--gold-dim, var(--gold-soft)); }
.site-footer__legal a:hover { color: var(--gold); }

/* ========================================================= data freshness ==
   Laid out as an answer rather than as a dump: a verdict, then the evidence.
   ======================================================================= */

.data__verdict { padding: 12px 14px; }
.data__line { margin: 0 0 6px; color: var(--text-dim); line-height: 1.55; }
.data__line:last-child { margin-bottom: 0; }
.data__line b { color: var(--ge-gold-2, var(--gold-soft)); }
/* A coloured bullet rather than coloured text: the sentence stays readable and
   the state is still scannable down the left edge. */
.data__line--good::before, .data__line--bad::before {
  content: ""; display: inline-block; vertical-align: 1px;
  width: 8px; height: 8px; border-radius: 50%; margin-right: 8px;
}
.data__line--good::before { background: #81c784; }
.data__line--bad::before  { background: #ff5252; }
.data__line--bad b { color: #ff8a80; }

.datatable { width: 100%; border-collapse: collapse; font-size: .85rem; }
.datatable thead th {
  text-align: left; padding: 7px 12px;
  background: var(--ge-head, var(--surface-2));
  color: var(--ge-gold, var(--gold));
  font-family: var(--font-head); font-size: .78rem;
  letter-spacing: .04em; text-transform: uppercase;
  border-bottom: 1px solid #000; white-space: nowrap;
}
.datatable td {
  padding: 7px 12px; border-top: 1px solid var(--ge-frame-2, var(--border));
  color: var(--text-dim); vertical-align: top;
}
.datatable tbody tr:nth-child(even) { background: rgb(255 255 255 / .022); }
.datatable .num { text-align: right; font-variant-numeric: tabular-nums; color: var(--text); }
.datatable__key { color: var(--ge-gold-2, var(--gold-soft)); white-space: nowrap; }
.datatable__note { font-size: .95em; }

.data__pill {
  display: inline-block; padding: 1px 7px; border-radius: 999px;
  font-size: .76rem; white-space: nowrap;
}
.data__pill--bad  { background: rgb(255 82 82 / .16); color: #ff8a80; border: 1px solid rgb(255 82 82 / .35); }
.data__pill--good { background: rgb(129 199 132 / .14); color: #a5d6a7; border: 1px solid rgb(129 199 132 / .32); }

/* The routing section: a copyable vhost snippet and the three live checks. */
.data__pre {
  margin: 8px 0 10px; padding: 10px 12px;
  background: rgb(0 0 0 / .35);
  border: 1px solid var(--ge-frame-2, var(--border));
  border-radius: var(--radius-sm);
  font-size: .78rem; line-height: 1.5; color: var(--text-dim);
  overflow-x: auto;
}
.data__probe { margin-top: 8px; }
.data__probe .data__line code { color: var(--text-faint); }
.data__line code {
  background: rgb(255 255 255 / .05); padding: 1px 5px; border-radius: 3px;
  font-size: .95em;
}

.data__counts {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(124px, 1fr));
  gap: 1px; background: var(--ge-frame-2, var(--border));
  border-top: 1px solid var(--ge-frame-2, var(--border));
}
.data__count { background: var(--ge-bg, var(--surface)); padding: 10px 12px; }
.data__count-n {
  display: block; font-size: 1.05rem; font-weight: 600;
  color: var(--ge-gold-2, var(--gold-soft)); font-variant-numeric: tabular-nums;
}
.data__count-l { display: block; font-size: .76rem; color: var(--text-faint); margin-top: 1px; }

.data__hint {
  margin: 0; padding: 10px 14px;
  font-size: .82rem; color: var(--text-faint); line-height: 1.6;
  border-top: 1px solid var(--ge-frame-2, var(--border));
}
.data__hint code {
  background: rgb(255 255 255 / .05); padding: 1px 5px; border-radius: 3px;
  color: var(--text-dim); font-size: .95em;
}

@media (max-width: 560px) {
  .datatable { font-size: .8rem; }
  .datatable td, .datatable thead th { padding: 6px 8px; }
  .datatable__note { display: none; }
}

/* A wide table scrolls inside its own panel rather than widening the page.
   Six columns of build log do not fit on a phone and there is no honest way to
   stack them, so the table keeps its shape and the panel takes the scroll -
   which is contained, obvious, and does not drag the whole layout sideways. */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table-wrap > .datatable { min-width: 100%; }
@media (max-width: 560px) {
  /* Below this the columns that are context rather than answer come off, and
     what is left fits without scrolling at all. */
  .datatable__opt { display: none; }
  .table-wrap > .datatable { min-width: 0; }
}

/* ============================================================== chatbox ==
   A tab docked to the bottom-right corner of every page: a title bar until
   you click it, then a log with a composer under it. The old site's #cb-main,
   in this site's brass and dark rather than its yellow.
   ======================================================================= */

.cb {
  position: fixed; right: 24px; bottom: 0; z-index: 60;
  width: min(380px, calc(100vw - 32px));
  font-size: .86rem;
  border: 1px solid var(--ge-frame-2, #4a4538);
  border-bottom: 0;
  border-radius: 8px 8px 0 0;
  background: var(--surface-2, #1b1812);
  box-shadow: 0 -4px 24px rgb(0 0 0 / .55);
  /* The panel below animates its height; without this the rounded top corners
     let the log's first row poke out over the title bar while it moves. */
  overflow: hidden;
}

/* --- the tab ----------------------------------------------------------- */

.cb__tab {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 7px 12px;
  background: linear-gradient(180deg, #3a3224, #241f16);
  border: 0; border-bottom: 1px solid var(--ge-frame-2, #4a4538);
  color: var(--gold, #ffd76a);
  font: inherit; font-weight: 600; letter-spacing: .02em;
  cursor: pointer; text-align: left;
}
.cb__tab:hover { background: linear-gradient(180deg, #463c2a, #2b251a); }
.cb__tab:focus-visible { outline: 2px solid var(--gold, #ffd76a); outline-offset: -2px; }
.cb__tab-title { flex: 1 1 auto; }

.cb__unread {
  flex: none; min-width: 1.35em; padding: 1px 5px;
  border-radius: 999px;
  background: var(--gold, #ffd76a); color: #1b1b1d;
  font-size: .72rem; font-weight: 700; text-align: center;
}

/* The caret points down when open, so it reads as "put it away". */
.cb__caret {
  flex: none; width: 0; height: 0;
  border-left: 5px solid transparent; border-right: 5px solid transparent;
  border-bottom: 6px solid currentColor;
  transition: transform .18s ease;
}
.cb[data-open="true"] .cb__caret { transform: rotate(180deg); }

/* Something arrived while it was shut. A slow pulse on the tab only - the old
   box flashed its whole background every second, which is hard to sit next to
   while reading an item page. */
@keyframes cb-pulse {
  0%, 100% { box-shadow: inset 0 0 0 0 rgb(255 215 106 / 0); }
  50%      { box-shadow: inset 0 0 14px 0 rgb(255 215 106 / .35); }
}
.cb--alert .cb__tab { animation: cb-pulse 2.4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .cb--alert .cb__tab { animation: none; background: linear-gradient(180deg, #4d4028, #2f2819); }
  .cb__caret { transition: none; }
}

/* --- the panel --------------------------------------------------------- */

.cb__panel {
  display: grid; grid-template-rows: 1fr auto;
  height: 0; opacity: 0;
  transition: height .2s ease, opacity .15s ease;
}
.cb[data-open="true"] .cb__panel {
  height: min(60vh, 420px);
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) { .cb__panel { transition: none; } }

.cb__list {
  overflow-y: auto; overscroll-behavior: contain;
  padding: 6px 0;
  scrollbar-width: thin;
  /* The log holds the newest message at the bottom and grows upward, so a
     short conversation sits on the composer rather than floating at the top.
     `margin-top: auto` on the first child, NOT justify-content: flex-end: on a
     scrollable flex column, flex-end pushes the overflow off the TOP, where
     scrollTop cannot reach it - which is why the box could not be scrolled back
     through its own history. The auto margin collapses to nothing once the
     content is taller than the box, so a long log simply scrolls. */
  display: flex; flex-direction: column;
}
.cb__list > :first-child { margin-top: auto; }
.cb__older {
  flex: none; align-self: center;
  margin: 2px 0 6px; padding: 3px 12px;
  background: var(--surface-2, #1b1812); color: var(--text-dim, #b9b3a5);
  border: 1px solid var(--border, #3a372f); border-radius: 999px;
  font: inherit; font-size: .74rem; cursor: pointer;
}
.cb__older:hover { color: var(--gold, #ffd76a); border-color: var(--gold-dim, #8a7433); }
.cb__older[disabled] { opacity: .5; cursor: default; }
.cb__list:focus-visible { outline: 2px solid var(--gold, #ffd76a); outline-offset: -2px; }

.cb__msg {
  padding: 5px 12px;
  line-height: 1.45;
  overflow-wrap: anywhere;
  /* flex-direction:column on the list would otherwise stretch every row to
     share the space; each message is exactly as tall as it needs to be. */
  flex: none;
}
.cb__msg + .cb__msg { border-top: 1px solid rgb(255 255 255 / .045); }
.cb__msg:hover { background: rgb(255 255 255 / .03); }
.cb__msg.is-pending { opacity: .5; }

/* The owner's delete and ban buttons, drawn by chat.js only in a browser
   holding the key. They sit in the corner of the message and stay invisible
   until the pointer is on that message, because for the one person who sees
   them they are always there and should never be the loudest thing in a
   conversation. Kept reachable by keyboard: :focus-within shows them too. */
.cb__msg { position: relative; }
.cb__mod {
  position: absolute; top: 3px; right: 7px;
  display: flex; gap: 3px;
  opacity: 0; transition: opacity .12s ease-out;
}
.cb__msg:hover .cb__mod,
.cb__msg:focus-within .cb__mod { opacity: 1; }
.cb__mod-btn {
  min-width: 19px; height: 19px; padding: 0 5px;
  background: rgb(0 0 0 / .45);
  border: 1px solid var(--line, #3a362e); border-radius: 3px;
  color: var(--text-faint); cursor: pointer;
  font: 600 .72rem/17px var(--font-body, sans-serif);
}
.cb__mod-btn:hover { color: #e8886a; border-color: rgb(255 130 95 / .5); }
/* Armed: the second click is the one that does it, so it stops being quiet. */
.cb__mod-btn.is-armed {
  width: auto; background: #7a2f26; border-color: #b4503f; color: #fff;
}
.cb__mod-btn[disabled] { opacity: .45; cursor: default; }
@media (prefers-reduced-motion: reduce) { .cb__mod { transition: none; } }

.cb__meta { display: flex; align-items: baseline; gap: 7px; }
.cb__who { color: var(--gold-soft, #e3c265); font-weight: 600; }
.cb__when {
  color: var(--text-faint, #8a8578); font-size: .72rem;
  font-variant-numeric: tabular-nums; cursor: help;
}
.cb__text { color: var(--text, #eceae4); margin-top: 1px; }
.cb__text a { color: var(--gold, #ffd76a); }

/* "/me did a thing" - the old box's one piece of syntax, and three rows of
   history use it. An action is not speech, so it runs on from the name. */
.cb__msg--action .cb__meta { display: inline-flex; }
.cb__msg--action .cb__text { display: inline; font-style: italic; color: var(--text-dim, #b9b3a5); }

.cb__emoji { vertical-align: text-bottom; width: auto; height: 1.2em; }
.cb__empty { padding: 28px 12px; text-align: center; color: var(--text-faint, #8a8578); }

/* Arrived while you were reading further up. */
.cb__jump {
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: 96px; z-index: 2;
  padding: 4px 12px; border-radius: 999px;
  background: var(--gold, #ffd76a); color: #1b1b1d;
  border: 0; font: inherit; font-size: .76rem; font-weight: 600;
  cursor: pointer; box-shadow: 0 2px 10px rgb(0 0 0 / .5);
}

/* --- the composer ------------------------------------------------------ */

.cb__form {
  padding: 8px 10px 10px;
  border-top: 1px solid var(--ge-frame-2, #4a4538);
  background: var(--surface-3, #221e17);
}
.cb__row { display: flex; gap: 6px; align-items: flex-start; }
.cb__row + .cb__row { margin-top: 6px; }
.cb__name { flex: 1 1 auto; min-width: 0; }
.cb__text-in {
  flex: 1 1 auto; min-width: 0;
  resize: none;
  /* Hidden, not auto: a one-line box whose content is one line still counts as
     overflowing by a pixel or two on Windows, and 'auto' answers that with a
     full scrollbar - arrows and all - sitting inside the composer. The class
     goes on only once autoGrow() has actually hit the 96px cap. */
  overflow-y: hidden;
  height: 2.2rem; max-height: 96px;
  font: inherit; line-height: 1.4;
}
.cb__text-in.is-full { overflow-y: auto; }

/* Thin, dark scrollbars inside the box. scrollbar-width covers Firefox; the
   ::-webkit rules cover Chrome and Edge, where the default is the wide light
   one with arrows on it. */
.cb *::-webkit-scrollbar { width: 8px; height: 8px; }
.cb *::-webkit-scrollbar-track { background: transparent; }
.cb *::-webkit-scrollbar-thumb {
  background: var(--ge-frame-2, #4a4538);
  border-radius: 4px;
  border: 2px solid transparent;
  background-clip: content-box;
}
.cb *::-webkit-scrollbar-thumb:hover { background: var(--gold-dim, #8a7433); background-clip: content-box; }
.cb *::-webkit-scrollbar-button { display: none; }
.cb__text-in { scrollbar-width: thin; scrollbar-color: var(--ge-frame-2, #4a4538) transparent; }
.cb__list { scrollbar-color: var(--ge-frame-2, #4a4538) transparent; }
.cb__send {
  flex: none; align-self: stretch;
  padding: 0 14px;
  background: linear-gradient(180deg, #e3c265, #b8933a);
  color: #1b1b1d; font: inherit; font-weight: 700;
  border: 1px solid #8a7433; border-radius: var(--radius, 4px);
  cursor: pointer;
}
.cb__send:hover { background: linear-gradient(180deg, #ffd76a, #c9a243); }
.cb__send:disabled { opacity: .5; cursor: default; }

.cb__emoji-btn {
  flex: none; padding: 0 10px; font-size: 1rem; line-height: 2.1rem;
  background: var(--surface-2, #1b1812); color: var(--gold-soft, #e3c265);
  border: 1px solid var(--border, #3a372f); border-radius: var(--radius, 4px);
  cursor: pointer;
}
.cb__emoji-btn:hover { color: var(--gold, #ffd76a); border-color: var(--gold-dim, #8a7433); }

.cb__picker {
  margin-top: 6px; padding: 6px 4px 6px 6px;
  max-height: 190px; overflow-y: auto;
  background: var(--surface-2, #1b1812);
  border: 1px solid var(--border, #3a372f); border-radius: var(--radius, 4px);
  scrollbar-width: thin;
}
/* A heading per group, sticky so you still know where you are three rows into
   the smileys. */
.cb__pick-group {
  position: sticky; top: -6px; z-index: 1;
  margin: 0 0 3px; padding: 4px 2px 3px;
  background: var(--surface-2, #1b1812);
  font-size: .68rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-faint, #8a8578);
}
.cb__pick-group:not(:first-child) { margin-top: 4px; border-top: 1px solid rgb(255 255 255 / .05); }
/* auto-fill rather than a fixed count: the box is 380px on a desktop and
   whatever the phone gives it otherwise, and the grid just fits more or fewer. */
.cb__pick-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(30px, 1fr));
  gap: 1px; margin-bottom: 2px;
}
.cb__pick {
  display: grid; place-items: center;
  aspect-ratio: 1 / 1;
  background: none; border: 1px solid transparent; border-radius: 4px;
  padding: 0; cursor: pointer;
  /* The emoji fonts by name, so Windows, macOS, Android and Linux each use
     their own colour set rather than falling back to a monochrome outline. */
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji",
               "Twemoji Mozilla", "Android Emoji", sans-serif;
  font-size: 1.22rem; line-height: 1;
}
.cb__pick:hover, .cb__pick:focus-visible {
  background: rgb(255 255 255 / .07);
  border-color: var(--gold-dim, #8a7433);
  outline: none;
}
.cb__pick:hover { border-color: var(--gold-dim, #8a7433); background: rgb(255 255 255 / .06); }
.cb__pick img { height: 20px; width: auto; }

/* `display:flex` above and the browser's own `[hidden] { display:none }` have
   the same specificity, so this sheet won on source order and the picker sat
   on screen as an empty strip above the composer whether it was open or shut.
   This has to come AFTER the rule it corrects. */
.cb__picker[hidden] { display: none; }

/* The minimap and what stands on it.
   Markers are positioned in percent and centred on their own point, so the
   image can be any size and they stay where they belong - the old page baked
   512-space pixels into the HTML and drew them on a 492-wide image. */
.map-image-box {
  position: relative;
  width: 100%; max-width: 512px; margin: 0 auto;
  aspect-ratio: 1 / 1;
  border: 1px solid var(--ge-frame-2, #4a4538); border-radius: var(--radius, 4px);
  overflow: hidden;
}
.map-image-box .map-image { display: block; width: 100%; height: 100%; }
.map-markers { position: absolute; inset: 0; pointer-events: none; }

.mk {
  position: absolute;
  transform: translate(-50%, -50%);
  pointer-events: auto;
  border-radius: 50%;
}
/* A spawn dot is small and semi-transparent on purpose: there are hundreds per
   zone and what you read is the SHAPE of the crowd, not any one point. */
.mk--monster {
  width: 4px; height: 4px;
  background: #e0574a; opacity: .62;
  box-shadow: 0 0 0 1px rgb(0 0 0 / .45);
}
.mk--boss {
  width: 11px; height: 11px; border-radius: 2px;
  background: var(--gold, #ffd76a);
  box-shadow: 0 0 0 1px #000, 0 0 7px rgb(255 215 106 / .8);
  transform: translate(-50%, -50%) rotate(45deg);
}
.mk--warp, .mk--shop, .mk--mission, .mk--npc {
  width: 11px; height: 11px;
  border: 2px solid #000;
  box-shadow: 0 0 0 1px rgb(255 255 255 / .35);
}
.mk--warp    { background: #5ab0e0; }
.mk--shop    { background: #7bd47b; }
.mk--mission { background: #c98ae0; }
.mk--npc     { background: #e3c265; }
.mk[title]:hover { filter: brightness(1.5); z-index: 2; }
/* A quest pin is numbered and deliberately the loudest thing on the image:
   unlike a spawn dot there are two or three of them and each one is a place
   you have to stand. */
.mk--quest {
  width: 17px; height: 17px;
  background: var(--gold, #ffd76a); color: #1b1b1d;
  font: 700 11px/17px var(--font-body, sans-serif); text-align: center;
  box-shadow: 0 0 0 1px #000, 0 0 8px rgb(255 215 106 / .85);
}
/* A script-only spot: same size so the numbers read together, cooler colour so
   it is obvious at a glance which pins the quest itself named and which ones
   came out of the server scripts. */
.mk--spot {
  width: 17px; height: 17px;
  background: #7fd1e8; color: #11232a;
  font: 700 11px/17px var(--font-body, sans-serif); text-align: center;
  box-shadow: 0 0 0 1px #000, 0 0 8px rgb(127 209 232 / .8);
}

/* --- quest guides ------------------------------------------------------- */
/* The Info Center tree, in the sidebar the other sections use for filters -
   on a phone that is the same drawer, which is right: here the tree IS the
   navigation. <details> does the folding, so the keyboard works for free. */
.qtree__search {
  width: 100%; margin-bottom: 10px; padding: 6px 8px;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.qtree { list-style: none; margin: 0; padding-left: 11px; }
.qtree[data-depth="0"] { padding-left: 0; }
.qtree li { margin: 1px 0; }
.qtree__group > summary {
  cursor: pointer; padding: 3px 0; font-size: .88rem; font-weight: 600;
  color: var(--text-dim);
}
.qtree__group > summary:hover { color: var(--gold); }
.qtree__quest {
  display: block; padding: 2px 0 2px 7px; font-size: .85rem;
  color: var(--text-dim); border-left: 2px solid transparent;
  overflow-wrap: anywhere;
}
.qtree__quest:hover { color: var(--gold); }
.qtree__quest.is-open {
  color: var(--gold); border-left-color: var(--gold); background: var(--surface-2);
}
/* One minimap per zone the quest visits, side by side where they fit. */
.qmaps {
  display: grid; gap: 14px; margin-top: 16px;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.qmaps h3 { margin: 0 0 6px; font-size: .85rem; color: var(--text-faint); }

/* --- script-only spawn positions ---------------------------------------- */
/* One block per spawner per zone. Flat rows were sixteen repetitions of one
   label; the grouping is what makes "one of these four" readable at a glance,
   so the block, not the row, carries the heading and the rule. */
.qspots {
  display: grid; gap: 10px; margin-top: 12px;
  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
}
.qspot {
  padding: 10px 12px 9px;
  background: rgb(255 255 255 / .022);
  border: 1px solid var(--line, #3a362e);
  border-left: 3px solid #7fd1e8;            /* the spot-pin colour */
  border-radius: 4px;
}
.qspot__head { margin: 0; font-size: .85rem; font-weight: 500; color: var(--text-dim, #b9b3a5); }
.qspot__what { font-weight: 700; color: var(--text, #e8e2d4); }
.qspot__rule {
  margin: 5px 0 8px; font-size: .78rem; color: #9fdcef;
}
.qspot__list { margin: 0; padding: 0; list-style: none; display: grid; gap: 3px; }
.qspot__pos {
  display: flex; align-items: center; gap: 8px;
  padding: 2px 4px; border-radius: 3px;
}
/* The number is the same chip as the pin on the image, so the eye pairs them
   without reading. position:static because .mk is absolutely placed on a map. */
.qspot__n {
  position: static; flex: none; transform: none; margin: 0;
}
.qspot__xyz {
  position: relative; cursor: copy;
  font: 400 .78rem/1.5 var(--font-mono, ui-monospace, monospace);
  color: var(--text, #e8e2d4);
}
.qspot__xyz:hover { color: #9fdcef; }
.qspot__xyz.is-copied::after {
  content: 'copied';
  margin-left: 7px; font: 400 .68rem/1 var(--font-body, sans-serif);
  color: #7fd1e8;
}
.qspot__stage { font-size: .72rem; color: var(--text-faint); }

/* Row <-> pin hover, both directions; see hot() in quests.js. */
.qspot__pos.is-hot { background: rgb(127 209 232 / .2); }
tr.is-hot > td { background: rgb(255 215 106 / .1); }
.mk.is-hot {
  transform: translate(-50%, -50%) scale(1.45);
  z-index: 3; filter: brightness(1.35);
}
.qspot__n.is-hot { transform: scale(1.15); }

@media (prefers-reduced-motion: no-preference) {
  .mk--quest, .mk--spot { transition: transform .12s ease-out; }
}

/* Toggling a layer off. One class per layer on the box, set by the legend. */
.map-image-box.hide-monster .mk--monster,
.map-image-box.hide-boss    .mk--boss,
.map-image-box.hide-warp    .mk--warp,
.map-image-box.hide-shop    .mk--shop,
.map-image-box.hide-mission .mk--mission,
.map-image-box.hide-npc     .mk--npc { display: none; }

.map-legend {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px;
  max-width: 512px; margin: 8px auto 0;
  font-size: .76rem; color: var(--text-dim, #b9b3a5);
}
.map-legend__item {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 7px 2px 4px;
  background: none; border: 1px solid transparent; border-radius: 999px;
  color: inherit; font: inherit; cursor: pointer;
}
.map-legend__item .mk { position: static; transform: none; flex: none; }
.map-legend__item .mk--boss { transform: rotate(45deg); }
.map-legend__item:hover { border-color: var(--border, #3a372f); }
.map-legend__item[aria-pressed="false"] { opacity: .4; text-decoration: line-through; }
.map-legend__scale { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; }
.map-legend__bar {
  width: 38px; height: 0;
  border-top: 2px solid var(--text-dim, #b9b3a5);
  border-left: 1px solid var(--text-dim, #b9b3a5);
  border-right: 1px solid var(--text-dim, #b9b3a5);
  height: 5px;
}

/* The enchantment page: one table per chip group, each under the anchor an
   item's chip line links to. scroll-margin-top is what keeps that anchor from
   landing under the sticky header - without it the heading you jumped to is the
   one thing you cannot see. */
.enchant__intro {
  margin: 0 0 14px; max-width: 70ch;
  color: var(--text-dim, #b9b3a5); font-size: .88rem; line-height: 1.5;
}
.enchant-groups { display: grid; gap: 18px; }
.enchant-group__name {
  scroll-margin-top: 90px;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px;
  margin: 0 0 6px; font-size: 1rem;
  color: var(--gold-soft, #e3c265);
}
.enchant-group__key {
  font-size: .72rem; font-weight: 400; letter-spacing: .04em;
  color: var(--text-faint, #8a8578);
}
/* The group you arrived at, marked for as long as the address names it. */
.enchant-group__name:target { color: var(--gold, #ffd76a); }
.enchant-group__name:target::before {
  content: ''; flex: none;
  width: 3px; align-self: stretch; min-height: 1em;
  background: var(--gold, #ffd76a); border-radius: 2px;
}
.enchant-table {
  width: 100%; border-collapse: collapse;
  background: var(--surface-2, #1b1812);
  border: 1px solid var(--border, #3a372f); border-radius: var(--radius, 4px);
  font-size: .85rem;
}
.enchant-table th, .enchant-table td {
  padding: 6px 10px; text-align: left;
  border-bottom: 1px solid rgb(255 255 255 / .045);
}
.enchant-table thead th {
  color: var(--text-dim, #b9b3a5); font-weight: 600;
  background: rgb(255 255 255 / .03);
}
.enchant-table tbody tr:last-child td { border-bottom: 0; }
.enchant-table tbody tr:hover { background: rgb(255 255 255 / .03); }
.enchant-range { font-style: normal; color: var(--gold-soft, #e3c265); }
.enchant-chance { white-space: nowrap; color: var(--text-dim, #b9b3a5); width: 1%; }

/* ------------------------------------------------------------------ relics
   The Relics page. Its tables borrow the Enchantment tab's shell (same
   surface, same hairline rows) because they are the same kind of object; what
   is new here is the source card, which has to carry a tier table, an odds
   figure and a line of flavour without any of the three shouting. */

.relic-toc { display: flex; flex-direction: column; gap: 2px; }
.relic-toc a {
  padding: 7px 10px; border-radius: var(--radius-sm, 4px);
  color: var(--text-dim, #b9b3a5); text-decoration: none; font-size: .9rem;
}
.relic-toc a:hover,
.relic-toc a:focus-visible { background: rgb(255 255 255 / .04); color: var(--gold, #ffd76a); }

.relic-intro { max-width: 68ch; color: var(--text-dim, #b9b3a5); line-height: 1.6; }
.relic-note  { max-width: 72ch; color: var(--text-faint); font-size: .86rem; line-height: 1.55; }
.relic-note em { font-style: normal; color: var(--text-dim, #b9b3a5); }

/* The three gates, right under the first paragraph. Not a table and not a
   switcher - just the one fact a reader needs before anything else: which of
   the three their character can actually wear. */
.relic-gates {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin: 12px 0 14px; padding: 0; list-style: none;
}
.relic-gates li {
  background: var(--surface-2, #1b1812);
  border: 1px solid var(--border, #3a372f); border-radius: 999px;
}
.relic-gates a {
  display: flex; align-items: baseline; gap: 7px;
  padding: 5px 11px; text-decoration: none; border-radius: 999px;
}
.relic-gates li:hover, .relic-gates li:focus-within { border-color: var(--border-strong, #54545d); }
.relic-gates li.is-on { border-color: var(--gold-dim, #a8880c); }
.relic-gates__name { font-weight: 600; color: var(--text-dim, #b9b3a5); font-size: .88rem; }
.relic-gates li.is-on .relic-gates__name { color: var(--gold, #ffd76a); }
.relic-gates__lv { font-size: .76rem; color: var(--text-faint); }

.relic-intro--small { font-size: .88rem; }

/* ---- the stage path ----------------------------------------------------
   Five stops and four steps. The step is a row of its own BETWEEN two stops,
   because that is what it is - the cost of going from one to the next - and in
   the table it had to live inside the row of the stage being left, which meant
   reading one row for where you stand and the next for where you land. */
.relic-path { margin: 0; padding: 0; list-style: none; }
.relic-step { position: relative; }
.relic-step__stage {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 10px;
  padding: 10px 13px;
  background: var(--surface-2, #1b1812);
  border: 1px solid var(--border, #3a372f); border-radius: var(--radius, 4px);
}
.relic-step__no {
  flex: none; width: 22px; height: 22px; align-self: center;
  display: grid; place-items: center;
  background: rgb(255 255 255 / .05); border-radius: 50%;
  color: var(--text-faint); font: 600 .74rem/1 var(--font-body, sans-serif);
}
.relic-step__name {
  font: 600 1rem/1.3 var(--font-head, Georgia, serif);
  color: var(--gold-soft, #e3c265); min-width: 7.5em;
}
.relic-step__gain { flex: 1 1 14em; color: var(--text-dim, #b9b3a5); font-size: .88rem; }
.relic-step__frag {
  flex: none; color: var(--text-faint); font-size: .78rem;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* The step itself. The rail is a border on the gap rather than a pseudo-element
   behind everything, so it cannot drift out of line with the stops. */
.relic-gap {
  display: flex; align-items: center; gap: 9px;
  margin-left: 24px; padding: 7px 0 7px 18px;
  border-left: 2px solid var(--border, #3a372f);
}
.relic-gap__arrow { color: var(--gold-dim, #a8880c); font-size: .95rem; line-height: 1; }
.relic-gap__bits { color: var(--text-faint); font-size: .82rem; line-height: 1.45; }
.relic-gap__bits b {
  color: var(--text-dim, #b9b3a5); font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* ---- "What it does" ----------------------------------------------------
   The client's own sentence, with its two blanks marked rather than filled.
   `n` is the stance skill the roll landed on - the client writes five
   sentences differing only in that digit, and the page prints one. `X` is the
   rolled value, which is three ranges in the three columns to its right.
   Both have to read as holes in the sentence, not as values, so they are the
   only part of the cell that is not body-coloured. */
/* min-width, because on a phone the sentence is the widest thing in the row
   and a column that is free to shrink turns six words into eight lines. The
   table already scrolls sideways; let it. */
.relic-effect {
  color: var(--text-dim, #b9b3a5); font-size: .86rem; line-height: 1.45;
  min-width: 19em;
}
.relic-slot {
  font-style: italic; font-weight: 600;
  color: var(--gold-soft, #e3c265);
}
.relic-x {
  font-weight: 700; color: var(--gold, #ffd76a);
  font-variant-numeric: tabular-nums;
}
/* The one-line key under the paragraph that introduces the table. */
.relic-key { display: inline-block; margin-top: 5px; color: var(--text-faint); font-size: .84em; }

/* ---- the source filter ------------------------------------------------- */
.relic-filter { display: flex; align-items: center; gap: 10px; margin: 0 0 14px; }
.relic-filter__input {
  flex: 1 1 260px; max-width: 360px; padding: 7px 11px;
  background: var(--surface, #16140f);
  border: 1px solid var(--border, #3a372f); border-radius: var(--radius-sm, 4px);
  color: var(--text); font: inherit; font-size: .88rem;
}
.relic-filter__input:focus-visible {
  outline: none; border-color: var(--gold-dim, #a8880c);
}
.relic-filter__count {
  color: var(--text-faint); font-size: .8rem; font-variant-numeric: tabular-nums;
}

.relic-card__label { min-width: 0; overflow-wrap: anywhere; }

@media (max-width: 560px) {
  .relic-step__stage { gap: 6px 10px; }
  .relic-step__name { min-width: 0; }
  .relic-step__frag { width: 100%; }
}

/* The family switcher. A row of plates rather than tabs: there is no panel
   hanging off them, they are three addresses, and the Superior level is the
   thing actually being chosen between - Ein at 2, Soph at 5, Aur at 8. */
.relic-fams { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0 14px; }
.relic-fam {
  display: flex; flex-direction: column; gap: 1px;
  padding: 7px 14px; min-width: 104px;
  background: var(--surface-2, #1b1812);
  border: 1px solid var(--border, #3a372f); border-radius: var(--radius, 4px);
  text-decoration: none;
}
.relic-fam:hover,
.relic-fam:focus-visible { border-color: var(--border-strong, #54545d); }
.relic-fam.is-on {
  border-color: var(--gold-dim, #a8880c);
  background: linear-gradient(rgb(255 204 0 / .08), rgb(255 204 0 / .02));
}
.relic-fam__name {
  font: 600 .92rem/1.25 var(--font-head, Georgia, serif);
  color: var(--text-dim, #b9b3a5);
}
.relic-fam.is-on .relic-fam__name { color: var(--gold, #ffd76a); }
.relic-fam__lv { font-size: .72rem; color: var(--text-faint); }

/* A link in running text on the Relics page - into the Items section, mostly.
   Underlined rather than coloured alone: these sit inside grey explanatory
   paragraphs where colour on its own is not enough to read as a link. */
.relic-link {
  color: var(--gold-soft, #e3c265); text-decoration: none;
  border-bottom: 1px solid rgb(227 194 101 / .3);
}
.relic-link:hover,
.relic-link:focus-visible { color: var(--gold, #ffd76a); border-bottom-color: currentColor; }
.relic-note--after { margin-top: 14px; }

.relic-h2 {
  margin: 30px 0 6px; padding-bottom: 6px;
  border-bottom: 1px solid var(--border, #3a372f);
  font: 600 1.15rem/1.3 var(--font-head, Georgia, serif);
  color: var(--gold, #ffd76a); scroll-margin-top: 90px;
}
.relic-h3 {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px;
  margin: 22px 0 10px; font: 600 .98rem/1.3 var(--font-head, Georgia, serif);
  color: var(--text); scroll-margin-top: 90px;
}
.relic-h3__meta { font: 400 .78rem/1 var(--font-body, sans-serif); color: var(--text-faint); }

/* One card per source. auto-fill rather than auto-fit: a single card left on a
   row should stay card-width, not stretch across the whole column. */
.relic-grid {
  display: grid; gap: 12px;
  /* 460px, not 330: a card holds a tier label, two columns of effect text and
     a percentage, and at 330 every one of those wrapped to a word per line. */
  grid-template-columns: repeat(auto-fill, minmax(460px, 1fr));
}
.relic-card {
  background: var(--surface-2, #1b1812);
  border: 1px solid var(--border, #3a372f); border-radius: var(--radius, 4px);
  padding: 11px 13px 9px; scroll-margin-top: 90px;
}
.relic-card:target { border-color: var(--gold-dim, #a8880c); }
.relic-card__name {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  margin: 0 0 8px; font: 600 .95rem/1.3 var(--font-head, Georgia, serif);
  color: var(--gold-soft, #e3c265);
}
.relic-card__odds {
  flex: none; font: 600 .76rem/1 var(--font-body, sans-serif);
  color: var(--text-dim, #b9b3a5); font-variant-numeric: tabular-nums;
}
/* What every tier of a source says, lifted out of the table so the rows carry
   only what the tier changes. */
.relic-card__shared {
  margin: 0 0 9px; padding-bottom: 8px;
  border-bottom: 1px solid rgb(255 255 255 / .06);
  color: var(--text-dim, #b9b3a5); font-size: .82rem; line-height: 1.5;
}
.relic-card__shared span { color: inherit; }
.relic-card__shared-label {
  display: block; margin: 7px 0 1px;
  color: var(--text-faint) !important; font-size: .72rem;
  text-transform: uppercase; letter-spacing: .03em;
}
.relic-card__flavour {
  margin: 8px 0 0; color: var(--text-faint); font-size: .8rem; font-style: italic;
}

.relic-tiers { width: 100%; border-collapse: collapse; font-size: .82rem; }
.relic-tiers th, .relic-tiers td {
  padding: 5px 6px; text-align: left; vertical-align: top;
  border-bottom: 1px solid rgb(255 255 255 / .045);
}
.relic-tiers thead th {
  padding-top: 0; color: var(--text-faint); font-weight: 600; font-size: .74rem;
  text-transform: uppercase; letter-spacing: .03em;
}
.relic-tiers tbody tr:last-child th,
.relic-tiers tbody tr:last-child td { border-bottom: 0; }
/* The tier and the chance take what they need and no more, so the two text
   columns get everything that is left. */
.relic-tiers .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; width: 1%; }
.relic-tiers .relic-tier { width: 1%; }
.relic-tiers thead th:nth-child(2),
.relic-tiers thead th:nth-child(3) { width: auto; }
.relic-tiers td span { color: inherit; }

/* The three tiers, in the colours the client's own tooltip uses for them. */
.relic-tier { white-space: nowrap; font-weight: 600; }
.relic-tier a { text-decoration: none; color: inherit; }
.relic-tier a:hover, .relic-tier a:focus-visible { text-decoration: underline; }
.relic-tier--1 { color: #7f8de8; }
.relic-tier--2 { color: #74e650; }
.relic-tier--3 { color: #faeab9; }

/* The wide tables - stages, constellations, growth. */
.relic-table {
  width: 100%; border-collapse: collapse;
  background: var(--surface-2, #1b1812);
  border: 1px solid var(--border, #3a372f); border-radius: var(--radius, 4px);
  font-size: .85rem;
}
.relic-table th, .relic-table td {
  padding: 7px 10px; text-align: left; vertical-align: top;
  border-bottom: 1px solid rgb(255 255 255 / .045);
}
.relic-table thead th {
  color: var(--text-dim, #b9b3a5); font-weight: 600;
  background: rgb(255 255 255 / .03); white-space: nowrap;
}
.relic-table tbody tr:last-child th,
.relic-table tbody tr:last-child td { border-bottom: 0; }
.relic-table tbody tr:hover { background: rgb(255 255 255 / .03); }
.relic-table tbody th { color: var(--gold-soft, #e3c265); font-weight: 600; white-space: nowrap; }
.relic-table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.relic-table--narrow { max-width: 620px; }
.table-wrap > .relic-table { min-width: 100%; }

.relic-stage__no {
  display: inline-block; min-width: 18px; margin-right: 4px;
  color: var(--text-faint); font-weight: 400; font-variant-numeric: tabular-nums;
}
/* The roll weight beside a constellation range: present, but never the thing
   you read first - the range is what a player is after. */
.relic-weight { margin-left: 6px; color: var(--text-faint); font-size: .74rem; }
.relic-warn { margin-left: 4px; color: #e8886a; font-weight: 700; cursor: help; }

@media (max-width: 560px) {
  .relic-grid { grid-template-columns: 1fr; }
  .relic-h2 { margin-top: 22px; }
}

/* The chip line in an item panel, now a link to its group above. */
.ge-chip-link { color: var(--gold-soft, #e3c265); text-decoration: none; }
.ge-chip-link:hover,
.ge-chip-link:focus-visible { color: var(--gold, #ffd76a); text-decoration: underline; }

/* The owner's key field: absent for everybody until a reserved name is typed
   into the name box, which is almost nobody and never twice on the same
   machine. [hidden] has to beat .cb__row's display:flex, and both are one
   class, so it is written after it rather than louder than it.

   It lines up exactly with the name field above it - an indent or a rule down
   its side makes the composer look broken rather than making the field look
   related. What marks it as the owner's is the gold tint on the field itself. */
.cb__key-row[hidden] { display: none; }
.cb__key {
  flex: 1 1 auto; min-width: 0;
  letter-spacing: .16em;
  border-color: var(--gold-dim, #8a7433);
  background: rgb(227 194 101 / .07);
}
.cb__key::placeholder { letter-spacing: normal; }

.cb__note { margin: 5px 0 0; min-height: 1.05em; font-size: .74rem; color: var(--text-dim, #b9b3a5); }
.cb__note--bad { color: var(--bad, #e06c5e); }

/* The honeypot. Off-screen rather than display:none, because some bots skip
   fields they can tell are hidden - and aria-hidden plus tabindex=-1 keeps it
   away from anyone reading the page properly. */
.cb__hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

@media (max-width: 560px) {
  .cb { right: 8px; left: 8px; width: auto; }
  .cb[data-open="true"] .cb__panel { height: min(70vh, 380px); }
}

/* The back-to-top button sits in the same corner, so it steps over the tab
   rather than under it. */
.cb ~ .to-top, .to-top { bottom: 56px; }

/* The alchemy line on an item panel: the same gold the enchant chip line uses,
   because both answer "what else does the game let me do with this". */
.ge-alchemy-line { color: var(--ge-gold-2, #d9a441); margin-top: 4px; }

/* A single-choice filter row. The control is round because only one of them
   can be on at a time - see radioRow() in render.js, which also makes a second
   click on the chosen row clear it, since a radio cannot turn itself off. */
.check--radio input { border-radius: 50%; }


/* Quest walkthrough panel: use the site's shared surfaces and gold accents. */
.quest-details {
  margin-top: 24px;
  padding: clamp(16px, 3vw, 26px);
  border: 1px solid var(--border);
  border-top: 3px solid var(--gold-dim);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow);
  overflow-wrap: anywhere;
}
.quest-details > h2 {
  margin: 0 0 18px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border);
  color: var(--gold-soft);
  font-family: var(--font-head);
  font-size: 1.4rem;
}
.quest-details__list { margin: 0; padding-left: 22px; }
.quest-details__list li { padding: 4px 0; line-height: 1.65; }
.quest-details__list li::marker { color: var(--gold); font-weight: 600; }

/* A step that has a stage dot behind it. The whole line is the target, because
   unlike a step with a grid reference there is nothing inside it to aim at, so
   it says what it is with a cursor and a tint rather than with a button nobody
   asked for.

   The highlight is painted with a box-shadow spread rather than padding: a
   padded <li> would shift its own ::marker, and only the pinned lines would
   shift, leaving the numbers in the margin out of line with each other.

   .is-hot and .is-locked are set by the pane's own handlers on everything that
   shares a pin, which is what makes this work in both directions - hovering a
   dot on the map lights the step that sends you there. */
.quest-details__list li:is([data-pin], [data-pins]) {
  cursor: pointer; border-radius: 2px;
  transition: background-color .15s ease, box-shadow .15s ease;
}
/* The second spread is the 1px line; the first is how far it stands off the
   text. Keep them one apart or the line stops being a line. */
.quest-details__list li:is([data-pin], [data-pins]):is(:hover, :focus-visible, .is-hot) {
  background: rgb(255 204 0 / .09);
  box-shadow: 0 0 0 2px rgb(255 204 0 / .09);
}
.quest-details__list li:is([data-pin], [data-pins]).is-locked {
  background: rgb(255 204 0 / .13);
  box-shadow: 0 0 0 2px rgb(255 204 0 / .13), 0 0 0 3px var(--gold-dim, #a8880c);
}
/* The tint above already shows the focus; a second ring around it would sit
   outside the line that is already marking it. */
.quest-details__list li:is([data-pin], [data-pins]):focus-visible { outline: none; }
.quest-details__stage { padding: 16px 0; border-top: 1px solid var(--border); }
.quest-details__stage h3 {
  margin: 0 0 10px;
  color: var(--gold-soft);
  font-size: 1rem;
  font-family: var(--font-head);
}
.quest-details__text { margin: 0; white-space: pre-line; line-height: 1.75; color: var(--text); }

/* Coordinate links share the stage pin's hover and persistent selection. */
.quest-coordinate { color: var(--gold-soft); text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 3px; border-radius: 3px; }
.quest-coordinate:hover, .quest-coordinate:focus-visible, .quest-coordinate.is-locked { color: var(--gold); background: var(--surface-3); outline: 2px solid var(--gold-dim); outline-offset: 3px; }
.qmaps .mk.is-hot, .qmaps .mk.is-locked { z-index: 5; outline: 3px solid var(--gold-soft); outline-offset: 4px; box-shadow: 0 0 0 10px rgb(255 204 0 / .22), 0 0 20px rgb(255 204 0 / .65); }
.qmaps .map-image-wrap { scroll-margin-top: 90px; }

/* Guide references show the named grid cell, distinct from exact stage pins. */
.qmaps .mk--grid {
  width: 8.073%; height: 8.073%; border-radius: 2px;
  display: grid; place-items: center; box-sizing: border-box;
  border: 1px dashed var(--gold-dim); background: rgb(255 204 0 / .10);
  color: #ffe88a; font: 700 .7rem var(--font-body); font-style: normal;
  text-shadow: 0 1px 2px #000; z-index: 1;
}
.qmaps .mk--grid.is-hot, .qmaps .mk--grid.is-locked {
  transform: translate(-50%, -50%); border: 2px solid var(--gold);
  background: rgb(255 204 0 / .32); outline-offset: 1px;
}

.news-admin, .news-admin-actions { padding: .6rem; display:flex; gap:.6rem; align-items:center; }
.news-admin[hidden] { display:none; }
.news-editor { width:min(46rem,90vw); max-height:90vh; overflow:auto; background:#222; color:#ddd; border:1px solid #756322; border-top:3px solid #b99b20; border-radius:6px; padding:1.5rem; }
.news-editor::backdrop { background:#000a; }
.news-editor label { display:block; margin:.8rem 0; }
.news-editor input, .news-editor textarea { display:block; box-sizing:border-box; width:100%; margin-top:.4rem; padding:.6rem; background:#141414; color:#eee; border:1px solid #665b37; }
.news-editor button { margin-right:.6rem; }
