/*
 * The editorial shell shared by the long-form pages — /tomes, /survival, /lore,
 * /tale, and the unlisted brand kit.
 *
 * Structure only, and it reads the same theme tokens the game does, so every
 * one of these pages reskins with the Skin picker. Page-specific components
 * (swatch grids, card galleries, rule tables) live with their page.
 */

html { font-size: var(--root-size); }

body {
  margin: 0;
  font-family: var(--font-ui);
  font-variant-numeric: lining-nums;
  color: rgb(var(--ui-text-300-rgb));
  background:
    radial-gradient(120% 70% at 50% -12%, var(--page-wash-top) 0%, transparent 62%),
    radial-gradient(100% 55% at 50% 112%, var(--page-wash-bottom) 0%, transparent 62%),
    var(--page-base);
  background-attachment: fixed;
}

.page { max-width: 52rem; margin: 0 auto; padding: 3.5rem 2rem 6rem; }

/* ── masthead ─────────────────────────────────────────────────────────── */
.page-head { text-align: center; padding-bottom: 2.5rem; }
.eyebrow { font-size: .64rem; letter-spacing: .34em; text-transform: uppercase;
           color: rgb(var(--ui-text-500-rgb)); }
.page-title { font-family: var(--font-display); font-size: 3.4rem; line-height: 1.05;
              margin: .5rem 0 0; font-weight: 500; letter-spacing: .03em;
              color: rgb(var(--ui-accent-hi-rgb)); }
.lede { max-width: 34rem; margin: 1rem auto 0; font-size: .95rem; line-height: 1.6;
        color: rgb(var(--ui-text-400-rgb)); }

/* ── the skin filter ──────────────────────────────────────────────────── */
.filter { position: sticky; top: 0; z-index: 20; margin: 0 -2rem 3rem; padding: .7rem 2rem;
          display: flex; flex-wrap: wrap; gap: .3rem 1.15rem; align-items: center;
          background: rgb(var(--ui-900-rgb) / .92); backdrop-filter: blur(10px);
          border-bottom: 1px solid rgb(var(--ui-600-rgb)); }
.filter__label { font-size: .6rem; letter-spacing: .22em; text-transform: uppercase;
                 color: rgb(var(--ui-text-500-rgb)); margin-right: .3rem; }
.filter__spacer { flex: 1; }
.filter a {
  /* Was .72rem in the 400-grey. That measured 7.4:1 — technically fine — but
   * 12px needs more weight than a ratio to read as a navigation bar rather
   * than as fine print. Bigger, one step brighter, and with a real hit area. */
  font-size: .95rem;
  color: rgb(var(--ui-text-300-rgb));
  text-decoration: none;
  padding: .3rem 0;
  border-bottom: 1px solid transparent;
}
.filter a:hover { color: rgb(var(--ui-accent-hi-rgb)); border-bottom-color: currentColor; }
.filter a[aria-current="page"] { color: rgb(var(--ui-accent-hi-rgb)); border-bottom-color: currentColor; }
.skin-btn { display: inline-flex; align-items: center; gap: .45rem; cursor: pointer;
            font: inherit; font-size: .78rem; padding: .3rem .6rem .3rem .35rem;
            border-radius: 2rem; border: 1px solid rgb(var(--ui-600-rgb));
            background: rgb(var(--ui-800-rgb)); color: rgb(var(--ui-text-400-rgb)); }
.skin-btn:hover { color: rgb(var(--ui-text-200-rgb)); border-color: rgb(var(--ui-text-500-rgb)); }
.skin-btn[aria-pressed="true"] { border-color: rgb(var(--ui-accent-rgb));
            background: rgb(var(--ui-accent-rgb) / .1); color: rgb(var(--ui-accent-hi-rgb)); }
.swatch-mini { display: flex; border-radius: 2rem; overflow: hidden; flex: none;
               box-shadow: inset 0 0 0 1px rgb(255 255 255 / .12); }
.swatch-mini i { width: .62rem; height: 1.15rem; display: block; }

/* ── sections ─────────────────────────────────────────────────────────── */
.sec { padding: 3rem 0 0; }
.kick { font-size: .62rem; letter-spacing: .26em; text-transform: uppercase;
        color: rgb(var(--ui-accent-rgb)); }
.sec h2 { font-family: var(--font-display); font-size: 2rem; font-weight: 500;
          margin: .35rem 0 .6rem; color: rgb(var(--ui-text-100-rgb)); letter-spacing: .01em; }
.sec h3 { font-family: var(--font-display); font-size: 1.2rem; font-weight: 500;
          margin: 1.8rem 0 .4rem; color: rgb(var(--ui-text-200-rgb)); }
.note, .sec p { max-width: 44rem; font-size: .92rem; line-height: 1.7; margin: 0 0 1rem;
                color: rgb(var(--ui-text-400-rgb)); }
.note b, .sec p b, .sec li b { color: rgb(var(--ui-text-200-rgb)); font-weight: 600; }
.sec code { font-family: ui-monospace, monospace; font-size: .84em;
            color: rgb(var(--ui-accent-hi-rgb)); }
.subhead { font-size: .6rem; letter-spacing: .2em; text-transform: uppercase;
           color: rgb(var(--ui-text-500-rgb)); margin: 2rem 0 .8rem; }

.sec ul, .sec ol { max-width: 44rem; padding-left: 1.1rem; margin: 0 0 1.2rem;
                   color: rgb(var(--ui-text-400-rgb)); }
.sec li { font-size: .92rem; line-height: 1.65; margin-bottom: .5rem; }
.sec li::marker { color: rgb(var(--ui-text-600-rgb)); }

/* A pulled-out rule — the thing that decides the argument. */
.pull { border-left: 2px solid rgb(var(--ui-accent-rgb)); padding: .1rem 0 .1rem 1rem;
        margin: 1.4rem 0; max-width: 44rem; }
.pull p { margin: 0; color: rgb(var(--ui-text-300-rgb)); font-size: .95rem; }

/* ── tables ───────────────────────────────────────────────────────────── */
/* `display: table` IS LOAD-BEARING, and it is a collision rather than tidiness.
   `deck.css` also owns `.tbl` -- it is the lane strip on the play screen, and
   it sets `display: flex`. These pages link BOTH stylesheets, deck.css first,
   and this rule never declared a display, so every editorial table on Tomes,
   Survival and Lore rendered as a flex box: rows became flex items and no
   column lined up with the one above it. Eight tables, invisible to every
   gate, because both selectors are reachable and each file is correct alone.
   Same family as `.rd` colliding with the Reader. Check a borrowed prefix. */
.tbl { display: table; width: 100%; max-width: 44rem; border-collapse: collapse; font-size: .88rem;
       margin: 0 0 1.4rem; }
.tbl th { text-align: left; font-weight: 500; font-size: .6rem; letter-spacing: .16em;
          text-transform: uppercase; color: rgb(var(--ui-text-500-rgb));
          padding: 0 .9rem .5rem 0; }
.tbl td { padding: .55rem .9rem .55rem 0; border-top: 1px solid rgb(var(--ui-600-rgb));
          color: rgb(var(--ui-text-400-rgb)); vertical-align: top; line-height: 1.55; }
.tbl td b { color: rgb(var(--ui-text-200-rgb)); }

/* ── shared bits ──────────────────────────────────────────────────────── */
.row { display: flex; flex-wrap: wrap; gap: 1rem; align-items: flex-end; }
.stack { display: flex; flex-direction: column; align-items: center; gap: .45rem; }
.cap { font-size: .6rem; color: rgb(var(--ui-text-500-rgb)); text-align: center;
       max-width: 8rem; line-height: 1.35; }

.foot { margin-top: 4rem; padding-top: 1.5rem; border-top: 1px solid rgb(var(--ui-600-rgb));
        font-size: .72rem; color: rgb(var(--ui-text-500-rgb));
        display: flex; gap: 1.2rem; flex-wrap: wrap; line-height: 1.6; }
.foot a { color: rgb(var(--ui-accent-rgb)); }

.toast { position: fixed; bottom: 1.5rem; left: 50%; transform: translateX(-50%);
         background: rgb(var(--ui-accent-rgb)); color: rgb(var(--ui-900-rgb));
         padding: .5rem 1rem; border-radius: 2rem; font-size: .78rem; font-weight: 600;
         opacity: 0; pointer-events: none; transition: opacity .2s; z-index: 40; }
.toast.on { opacity: 1; }

@media (max-width: 46rem) {
  .page { padding: 2rem 1.1rem 4rem; }
  .filter { margin: 0 -1.1rem 2rem; padding: .55rem 1.1rem; gap: .3rem .9rem; }
  .page-title { font-size: 2.4rem; }
  /* This bar is the only navigation on these pages now, so it has to survive
   * narrow. Only the label goes. */
  .filter__label { display: none; }
  .filter a { font-size: .92rem; }
}

/* ── the masthead mark ─────────────────────────────────────────────────────
 * These pages had no logo, which made them read as documentation about the
 * product rather than part of it. Links home, like the game's wordmark. */
.page-mark { display: block; margin: 0 auto 1.6rem; width: min(17rem, 62vw); }
.page-mark img { display: block; width: 100%; height: auto; }

/* ── the hamburger, injected by pagenav.js ─────────────────────────────────
 * Button LEFT, drawer LEFT — the game's arrangement, so moving between the
 * two never flips which side the world opens from. */
.pagenav__btn {
  /* Desktop: a BARE glyph in the gutter, taken out of flow, so the WORDMARK is
     the first thing on the shared indent and the X of XENOCIDE lines up with
     the T of the spine title. The bordered box was ditched here on purpose --
     it pushed the wordmark right by its own border and box. On a phone the box
     comes back as a tap area (see the narrow block).*/
  position: absolute; left: 1rem; top: 50%; transform: translateY(-50%);
  font: inherit; font-size: 1.5rem; line-height: 1; cursor: pointer;
  padding: .1rem .25rem; border: 0; background: none;
  color: rgb(var(--ui-text-400-rgb));
}
.pagenav__btn:hover { color: rgb(var(--ui-accent-hi-rgb)); }

.pagenav__scrim {
  position: fixed; inset: 0; z-index: 40; background: rgb(0 0 0 / .45);
  opacity: 0; pointer-events: none; transition: opacity .18s;
}
/* Positioning and motion only. Width, surface and every row come from .menu
   in deck.css, which these pages already link — the drawer is the game's menu,
   not a second one that resembles it. */
.pagenav {
  position: fixed; left: 0; top: 0; bottom: 0; z-index: 41;
  max-width: 84vw;
  transform: translateX(-100%); transition: transform .2s ease;
  display: flex; flex-direction: column;
}
.pagenav-open .pagenav__scrim { opacity: 1; pointer-events: auto; }
.pagenav-open .pagenav { transform: none; }

.pagenav__close {
  margin-left: auto; font: inherit; font-size: 1.2rem; line-height: 1; cursor: pointer;
  background: none; border: 0; color: rgb(var(--ui-text-500-rgb)); padding: 0 .2rem;
}
.pagenav__close:hover { color: rgb(var(--ui-text-200-rgb)); }

/* With the drawer available, the inline links are a wide-screen convenience. */
@media (max-width: 46rem) {
  .filter a:not(.skin-btn) { display: none; }
  .page-mark { width: min(13rem, 66vw); margin-bottom: 1.1rem; }
}

/* ── glossary terms and their modal ────────────────────────────────────────
 * A term is a quiet dotted underline, not a link colour: these appear several
 * times a paragraph and full link styling would turn the Tomes into soup. */
.gl {
  cursor: help;
  border-bottom: 1px dotted rgb(var(--ui-accent-rgb) / .65);
  color: rgb(var(--ui-text-200-rgb));
}
.gl:hover, .gl:focus-visible {
  color: rgb(var(--ui-accent-hi-rgb));
  border-bottom-style: solid;
  outline: none;
}

.gmodal { position: fixed; inset: 0; z-index: 60; display: none; }
.gmodal-open .gmodal { display: block; }
.gmodal__scrim { position: absolute; inset: 0; background: rgb(0 0 0 / .55); backdrop-filter: blur(2px); }
.gmodal__card {
  position: relative;
  width: min(30rem, calc(100vw - 2rem));
  margin: min(18vh, 8rem) auto 0;
  padding: 1.5rem 1.6rem 1.4rem;
  border-radius: 10px;
  background: rgb(var(--ui-800-rgb));
  border: 1px solid rgb(var(--ui-600-rgb));
  box-shadow: 0 24px 60px var(--drop-shadow);
  animation: gm-in .16s ease-out both;
}
@keyframes gm-in { from { opacity: 0; transform: translateY(.5rem) } to { opacity: 1; transform: none } }
.gmodal__x {
  position: absolute; top: .5rem; right: .6rem;
  font: inherit; font-size: 1.4rem; line-height: 1; cursor: pointer;
  background: none; border: 0; padding: .1rem .3rem;
  color: rgb(var(--ui-text-500-rgb));
}
.gmodal__x:hover { color: rgb(var(--ui-text-100-rgb)); }
.gmodal__kind {
  margin: 0; font-size: .58rem; letter-spacing: .22em; text-transform: uppercase;
  color: rgb(var(--ui-accent-rgb));
}
.gmodal__title {
  font-family: var(--font-display); font-size: 1.7rem; font-weight: 500;
  margin: .3rem 0 .7rem; color: rgb(var(--ui-text-100-rgb));
}
.gmodal__body { margin: 0; font-size: .92rem; line-height: 1.65; color: rgb(var(--ui-text-300-rgb)); max-width: none; }
.gmodal__see { margin: 1rem 0 0; font-size: .78rem; color: rgb(var(--ui-text-500-rgb)); }
.gmodal__see a { color: rgb(var(--ui-accent-rgb)); text-decoration: none; border-bottom: 1px dotted currentColor; }
.gmodal__see a:hover { color: rgb(var(--ui-accent-hi-rgb)); }
.gmodal__dot { margin: 0 .5rem; color: rgb(var(--ui-text-600-rgb)); }
@media (prefers-reduced-motion: reduce) { .gmodal__card { animation-duration: .01ms; } }

/* ── the scale ─────────────────────────────────────────────────────────────
 * Ratios lifted from helmur.com/manifesto, measured rather than guessed:
 *
 *     h1 72 / h2 36 / lead 24 / body 18 / label 12     (at a 16px root)
 *
 * Two things there matter more than the numbers. The h1:h2 step is exactly
 * 2:1 — ours was 1.4:1, which is why the old page read flat. And headings run
 * TIGHT: line-height 1.0, tracking −0.025em. Our old page-title had *positive*
 * tracking, which at display size loosens a serif into mush.
 *
 * Retuned in two places. Our root is 17px because EB Garamond has a small
 * x-height, so everything is expressed in rem against that and lands within a
 * pixel of the reference. And the weight is 600, not 700: Garamond's bold is
 * heavier than a grotesk's and 700 goes lumpy above 40px.
 */
.page-title {
  font-size: clamp(2.9rem, 7.5vw, 4.24rem);   /* 72px at the top end */
  line-height: 1;
  letter-spacing: -.02em;
  font-weight: 600;
}
.lede { font-size: 1.18rem; line-height: 1.5; max-width: 36rem; }

.sec--teach h2 {
  font-size: clamp(1.85rem, 4vw, 2.12rem);    /* half the h1, as in the reference */
  line-height: 1.11;
  letter-spacing: -.018em;
  font-weight: 600;
  margin: .1rem 0 .6rem;
}
.sec--teach h3 { font-size: 1.45rem; line-height: 1.25; margin: 2.4rem 0 .5rem; }
.sec--teach p, .sec--teach li { font-size: 1.06rem; line-height: 1.65; }
.sec--teach .lead {
  font-size: 1.41rem; line-height: 1.35; max-width: 40rem;
  color: rgb(var(--ui-text-300-rgb)); margin-bottom: 1.5rem;
}

/* The section number is a small accent label ABOVE the heading — the
 * reference's pattern, and a quieter one than the large ghosted numeral this
 * replaced. */
.sec--teach .kick {
  display: block; font-size: .71rem; letter-spacing: .2em; font-weight: 500;
  margin-bottom: .55rem;
}
.kick__dot { opacity: .5; margin: 0 .15em; }

/* A rule between sections, and real air around it. */
.sec--teach { padding-top: 3.6rem; margin-top: 3.6rem; border-top: 1px solid rgb(var(--ui-600-rgb)); }
.sec--teach:first-of-type { border-top: 0; margin-top: 0; padding-top: 1rem; }

/* The measure. 720px in the reference; 42rem is the same at our root. */
.sec--teach p, .sec--teach li, .sec--teach .tbl,
.sec--teach ul, .sec--teach ol, .sec--teach .pull { max-width: 42rem; }

/* ── the four steps ────────────────────────────────────────────────────── */
.steps { counter-reset: step; margin: 1.4rem 0 0; padding: 0; list-style: none; max-width: 44rem; }
.step {
  counter-increment: step;
  position: relative; padding: 0 0 1.5rem 3.4rem; margin: 0;
  border-left: 1px solid rgb(var(--ui-600-rgb)); margin-left: 1.1rem;
}
.step:last-child { border-left-color: transparent; padding-bottom: 0; }
.step::before {
  content: counter(step);
  position: absolute; left: -1.1rem; top: -.15rem;
  width: 2.2rem; height: 2.2rem; border-radius: 50%;
  display: grid; place-items: center;
  font-family: var(--font-display); font-variant-numeric: lining-nums;
  font-size: 1.05rem; font-weight: 600;
  color: rgb(var(--ui-900-rgb)); background: rgb(var(--ui-accent-rgb));
}
.step h4 {
  margin: 0 0 .35rem; font-family: var(--font-display); font-weight: 500;
  font-size: 1.25rem; color: rgb(var(--ui-text-100-rgb));
}
.step p { margin: 0 0 .5rem; }

/* ── worked examples ───────────────────────────────────────────────────── */
.demo {
  display: flex; flex-wrap: wrap; gap: 1.4rem; align-items: center;
  margin: 1.5rem 0; padding: 1.1rem 1.3rem; max-width: 44rem;
  border-radius: 9px;
  background: rgb(var(--ui-800-rgb) / .7);
  border: 1px solid rgb(var(--ui-600-rgb));
}
.demo__cards { display: flex; gap: .5rem; align-items: flex-end; flex: none; }
.demo__op { align-self: center; color: rgb(var(--ui-text-500-rgb)); font-size: 1.3rem; }
.demo__math { flex: 1; min-width: 12rem; }
.demo__sum {
  display: block; font-family: var(--font-display);
  font-variant-numeric: lining-nums; font-size: 1.3rem;
  color: rgb(var(--ui-accent-hi-rgb)); margin-bottom: .4rem;
}
.demo__math ul { margin: 0; padding-left: 1rem; }
.demo__math li { font-size: .9rem; line-height: 1.6; margin-bottom: .2rem; }
.demo__verdict { font-size: .86rem; color: rgb(var(--ui-text-500-rgb)); margin-top: .5rem; }
.demo--bad { border-color: rgb(var(--ui-600-rgb)); opacity: .96; }

/* two panels side by side — do this / not that */
.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; max-width: 44rem; margin: 1.4rem 0; }
.pair > * { margin: 0 !important; }
@media (max-width: 46rem) { .pair { grid-template-columns: 1fr; } }

/* ── the glossary page ─────────────────────────────────────────────────── */
.gloss { margin: 1.2rem 0 0; max-width: 44rem; }
.gloss dt {
  font-family: var(--font-display); font-size: 1.3rem; font-weight: 500;
  color: rgb(var(--ui-text-100-rgb));
  margin-top: 1.6rem; display: flex; align-items: baseline; gap: .8rem; flex-wrap: wrap;
  scroll-margin-top: 5rem;         /* the filter bar is sticky */
}
.gloss dt:target { color: rgb(var(--ui-accent-hi-rgb)); }
.gloss__kind {
  font-family: var(--font-ui); font-size: .58rem; letter-spacing: .2em;
  text-transform: uppercase; color: rgb(var(--ui-accent-rgb));
}
.gloss dd {
  margin: .35rem 0 0; font-size: .96rem; line-height: 1.75;
  color: rgb(var(--ui-text-400-rgb));
}
.gloss__see { display: block; margin-top: .4rem; font-size: .8rem; color: rgb(var(--ui-text-500-rgb)); }
.gloss__see a { color: rgb(var(--ui-accent-rgb)); text-decoration: none; border-bottom: 1px dotted currentColor; }
.gloss__see a:hover { color: rgb(var(--ui-accent-hi-rgb)); }

/* ── The Record ────────────────────────────────────────────────────────────
   An index of stubs plus a page per entry. The index shows date, area, title
   and one line, and nothing else: the job of a changelog index is to let
   somebody find the change they care about, not to make them read all of it.

   A dated list wants a date column rather than a heading that repeats the
   date. On anything narrow the column collapses above the entry, because two
   columns inside 375px leaves the prose about eleven characters wide. */
.rel { display: grid; grid-template-columns: 9.5rem 1fr; gap: 0 2rem;
       padding: 1.9rem 0; border-top: 1px solid rgb(var(--rule-rgb)); }
.rel:first-of-type { border-top: 0; padding-top: .6rem; }
.rel[hidden] { display: none; }
.rel__when { display: flex; flex-direction: column; gap: .35rem;
             padding-top: .5rem; font-size: .68rem; letter-spacing: .18em;
             text-transform: uppercase; color: rgb(var(--ui-text-600-rgb));
             font-variant-numeric: lining-nums tabular-nums; }
.rel__when time { white-space: nowrap; }

/* The area label. Bordered rather than filled: five of these down the left of
   a page is a lot of weight if each one is a solid block of accent. */
.rel__cat { align-self: flex-start; border: 1px solid rgb(var(--rule-rgb));
            border-radius: 2px; padding: .18rem .45rem; font-size: .6rem;
            letter-spacing: .16em; color: rgb(var(--ui-text-300-rgb)); }

.rel__body h2 { margin-top: 0; margin-bottom: .45rem; }
.rel__body h2 a { color: inherit; text-decoration: none; }
.rel__body h2 a:hover { color: rgb(var(--ui-accent-rgb)); }
.rel__sum { max-width: 44rem; margin: 0 0 .7rem; font-size: .95rem;
            line-height: 1.65; color: rgb(var(--ui-text-300-rgb)); }
.rel__more { font-size: .78rem; letter-spacing: .04em;
             color: rgb(var(--ui-accent-rgb)); text-decoration: none;
             border-bottom: 1px solid transparent; }
.rel__more:hover { border-bottom-color: rgb(var(--ui-accent-rgb)); }

.rec-cat { font-family: inherit; font-size: .7rem; letter-spacing: .14em;
           text-transform: uppercase; padding: .4rem .75rem; cursor: pointer;
           border: 1px solid rgb(var(--rule-rgb)); border-radius: 3px;
           background: none; color: rgb(var(--ui-text-600-rgb)); }
.rec-cat:hover { color: rgb(var(--ui-text-200-rgb));
                 border-color: rgb(var(--ui-text-600-rgb)); }
.rec-cat.is-on { color: rgb(var(--ui-accent-rgb));
                 border-color: rgb(var(--ui-accent-rgb)); }
.rec-empty { color: rgb(var(--ui-text-600-rgb)); font-size: .9rem;
             padding: 2rem 0; }

/* ── One entry ─────────────────────────────────────────────────────────── */
.page-head--entry .page-title { max-width: 34rem; margin-left: auto;
                                margin-right: auto; }
.eyebrow a { color: inherit; text-decoration: none; }
.eyebrow a:hover { color: rgb(var(--ui-accent-rgb)); }
.entry-meta { display: flex; align-items: center; justify-content: center;
              flex-wrap: wrap; gap: .7rem; margin: 1rem 0 0;
              font-size: .68rem; letter-spacing: .18em; text-transform: uppercase;
              color: rgb(var(--ui-text-600-rgb));
              font-variant-numeric: lining-nums tabular-nums; }
.entry-by { color: rgb(var(--ui-text-300-rgb)); }

/* Markdown output. The rendered body has no classes of its own, so the entry
   page styles the elements directly rather than asking an author to remember
   any markup at all. */
.entry-body > :first-child { margin-top: 0; }
.entry-body p, .entry-body li { max-width: 44rem; }
.entry-body h2 { margin-top: 2.4rem; }
.entry-body h3 { margin-top: 1.8rem; }
.entry-body em { color: rgb(var(--ui-text-200-rgb)); }
.entry-body hr { border: 0; border-top: 1px solid rgb(var(--rule-rgb));
                 margin: 2.4rem 0; }
.entry-body blockquote { border-left: 2px solid rgb(var(--ui-accent-rgb));
                         padding: .1rem 0 .1rem 1rem; margin: 1.4rem 0;
                         max-width: 44rem; }
.entry-body blockquote p { color: rgb(var(--ui-text-300-rgb)); }

.entry-nav { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;
             margin: 3.5rem 0 0; padding-top: 1.6rem;
             border-top: 1px solid rgb(var(--rule-rgb)); }
.entry-nav__side { display: flex; flex-direction: column; gap: .3rem;
                   text-decoration: none; }
.entry-nav__side--r { text-align: right; }
.entry-nav__dir { font-size: .62rem; letter-spacing: .2em; text-transform: uppercase;
                  color: rgb(var(--ui-text-600-rgb)); }
.entry-nav__title { font-family: var(--font-display); font-size: 1.05rem;
                    color: rgb(var(--ui-text-200-rgb)); line-height: 1.3; }
.entry-nav__side:hover .entry-nav__title { color: rgb(var(--ui-accent-rgb)); }

@media (max-width: 46rem) {
  .rel { grid-template-columns: 1fr; gap: 0; padding: 1.5rem 0; }
  .rel__when { flex-direction: row; align-items: center; gap: .6rem;
               padding: 0 0 .5rem; }
  .entry-nav { grid-template-columns: 1fr; }
  .entry-nav__side--r { text-align: left; }
}

/* ══ The long-form shell, second pass ══════════════════════════════════════
   These pages are read once and then CONSULTED, often mid-game, so they are
   built to be used rather than merely read. Five changes, each measured
   against what it replaced:

     · a 264px spine listing every section, built by spine.js from the page's
       own headings, replacing "scroll and hope"
     · one measure everywhere (47rem, about 68 characters) instead of a column
       that was whatever the window happened to be
     · a 4.5rem bar instead of a hero that cost a whole viewport before the
       first rule, on a page you return to constantly
     · one alignment. The head centred and the body did not; centred
       ragged-right prose at this length is work
     · one rhythm: 2.5rem and a hairline between sections, 1rem inside them

   Colour comes from the front-of-house palette (themes.css `.entry`), so
   `.page` carries that class. These pages are already locked to Obsidian. */

.page.entry {
  display: block; max-width: none; margin: 0; padding: 0;
  background: radial-gradient(100% 60% at 50% 0%,
              var(--e-void-hi) 0%, #080C0B 42%, var(--e-void) 100%);
  min-height: 100%;
}

/* ── the bar ────────────────────────────────────────────────────────────── */
.topbar {
  /* It is a child of the grid too, so without this it takes the rail's column
     and pushes the spine and the prose out of place. */
  grid-column: 1 / -1;
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; gap: .9rem;
  /* 1rem on the left, which is the game header's own px-4, so the menu
     button lands on exactly the same vertical in both. It briefly sat at
     1.875rem to line up with the spine's title beneath it; matching the game
     matters more, because that is the control people learn first. */
  padding: .875rem 2.5rem .875rem var(--nav-indent);
  position: relative;
  background: #0A0F0D;
  border-bottom: 1px solid rgb(var(--e-rule) / .08);
}
.topbar__mark { display: block; width: 9.375rem; height: auto; }
/* THE BREADCRUMB. The horizontal page row is gone -- the hamburger drawer
   already holds every page, grouped, so a second copy in the masthead was pure
   duplication. What the bar still owes the reader is where they are, so the
   current page's name sits beside the mark, injected by pagenav.js from the one
   list of links. The chevron is drawn, not typed, and rides the verdigris. */
.topbar__here { flex: 1; display: inline-flex; align-items: baseline; gap: .7rem;
  font-family: var(--font-display); font-size: 1.15rem; color: var(--e-bone);
  letter-spacing: .005em; }
.topbar__here::before { content: "\203A"; font-family: var(--font-mono);
  font-size: .9rem; color: rgb(var(--e-verdigris)); position: relative; top: -.05rem; }
/* The two actions, opposite the pages. `margin-left: auto` rather than a
   flex-1 nav, so the links stay bunched at the mark instead of stretching
   across a wide window. */
.topbar__acts { margin-left: auto; display: flex; align-items: center; gap: .625rem; }

/* Borrowed from the game without the game's reset: these pages do not load
   Tailwind, so nothing sets border-box and `.menu__row { width: 100% }` plus
   the boxed row's padding and border overflowed the drawer by exactly 7px.
   Scoped to what this file injects rather than reset globally, because a
   blanket reset on pages that were authored without one moves other things. */
.topbar__acts, .topbar__acts *, .pagenav, .pagenav * { box-sizing: border-box; }

.tbtn {
  font-size: .9375rem; line-height: 1; white-space: nowrap;
  padding: .5625rem 1rem; border-radius: .375rem; text-decoration: none;
  border: 1px solid transparent; transition: background .14s, border-color .14s, color .14s;
}
/* The one action the site exists to lead to, in the app's established primary
   -- the same solid accent the contact form sends with. */
.tbtn--go {
  background: rgb(var(--ui-accent-rgb)); color: rgb(var(--ui-900-rgb)); font-weight: 600;
}
.tbtn--go:hover { background: rgb(var(--ui-accent-hi-rgb)); }
/* Subdued on purpose, and a DIFFERENT SHAPE of button rather than a paler one:
   a fill beside a fill reads as two equals whatever the two colours are. */
.tbtn--quiet {
  color: var(--e-body); border-color: rgb(var(--ui-text-600-rgb) / .4);
}
.tbtn--quiet:hover { color: var(--e-bone); border-color: rgb(var(--ui-text-600-rgb) / .75); }
.tbtn--quiet[aria-current="page"] { color: var(--e-bone); border-color: rgb(var(--e-verdigris)); }

/* ── spine + measure ────────────────────────────────────────────────────── */
/* Two classes, or `.page.entry`'s display:block outranks it and the rail lands
   on top of the prose instead of beside it. */
.page.entry.page--spined {
  display: grid; grid-template-columns: 16.5rem minmax(0, 1fr); align-items: start;
  /* The X of the wordmark and the T of the spine title share this edge; the
     menu button hangs in the gutter to its left. */
  --nav-indent: 3rem;
}
.spine {
  position: sticky; top: 4.25rem; align-self: start;
  border-right: 1px solid rgb(var(--e-rule) / .08);
  padding: 2.125rem 1.75rem 2.5rem var(--nav-indent);
  display: flex; flex-direction: column; gap: 1.375rem;
  max-height: calc(100vh - 4.25rem); overflow-y: auto;
}
.spine__head { display: flex; flex-direction: column; gap: .5rem; }
.spine__title { font-family: var(--font-display); font-size: 1.625rem; line-height: 1.1;
                color: var(--e-bone); }
.spine__meta { font-family: var(--font-mono); font-size: .6875rem; letter-spacing: .16em;
               text-transform: uppercase; color: var(--e-label); }
.spine__list { list-style: none; margin: 0; padding: 0;
               display: flex; flex-direction: column; gap: 1px; }
.spine__list a {
  display: flex; gap: .75rem; align-items: baseline;
  padding: .5rem 0 .5rem .75rem; text-decoration: none;
  border-left: 2px solid transparent;
}
.spine__n { font-family: var(--font-mono); font-size: .6875rem; color: var(--e-legal);
            width: 1.25rem; flex: none; }
.spine__t { font-family: var(--font-display); font-size: 1rem; line-height: 1.3;
            color: var(--e-body); }
.spine__list a:hover .spine__t { color: var(--e-bone); }
.spine__list a.is-here { border-left-color: rgb(var(--e-verdigris));
                         background: rgb(var(--e-verdigris) / .07); }
.spine__list a.is-here .spine__t { color: var(--e-bone); }
.spine__list a.is-here .spine__n { color: var(--e-verd-lit); }
.spine-links { display: flex; flex-direction: column; gap: .5625rem;
               padding-top: 1.125rem; border-top: 1px solid rgb(var(--e-rule) / .08); }

/* 44.7rem against the 17px root is the design's 760px, which is about 68
   characters of EB Garamond at this size. The cap is on the TEXT, so the
   padding sits outside it and the measure never changes with the window. */
/* Centred in the column, not shoved against the rail. The measure is still
   44.7rem — that is the argued reading width and it does not move — but it now
   sits in the middle of the space left over, so every page has air on both
   sides. The Chronicle already looked like this because its children are 80%
   and centred; everything else started hard against the spine. */
.page-body {
  padding: 2.125rem 4rem 3rem; max-width: 44.7rem;
  /* Left-aligned with a deliberate gutter rather than centred. Centring split
     the column's slack evenly and put 199px between the rail and the first
     word, which reads as a hole rather than as breathing room. */
  margin-inline: 0 auto;
}
@media (min-width: 60rem) { .page-body { padding-left: 7.5rem; } }

/* ── rhythm and alignment ───────────────────────────────────────────────── */
.page.entry .page-head { text-align: left; padding: 0 0 0; }
/* The title is SHOWN, and the rule that hid it was a leftover.
   It said "it lives in the spine", and it did: the spine used to carry the
   entry's title. Then the spine was repurposed to carry the index's category
   rail so a reader could get back to a filtered view, and this rule stayed
   behind. The result was an entry with no visible title anywhere on it -- the
   <title> tag and the og: tags still had it, so every shared link looked
   right and the page itself said nothing. Nothing reports a heading that is
   merely absent. */
.page.entry .lede {
  margin: 0; max-width: none; font-size: 1.375rem; line-height: 1.62;
  color: var(--e-tagline);
}
.page.entry .lede b, .page.entry .lede strong { color: var(--e-bone); font-weight: 400; }
.page.entry .sec {
  padding: 2.375rem 0 0; margin-top: 2.375rem;
  border-top: 1px solid rgb(var(--e-rule) / .08);
  /* The bar is sticky, so a jumped-to heading would land underneath it. */
  scroll-margin-top: 5.5rem;
}
.page.entry .sec:first-of-type { border-top: 0; }
.page.entry .kick {
  font-family: var(--font-mono); font-size: .6875rem; letter-spacing: .22em;
  text-transform: uppercase; color: var(--e-label); margin: 0 0 1rem;
}
.page.entry .sec h2 {
  font-size: 2.375rem; line-height: 1.12; font-weight: 600;
  color: var(--e-bone); margin: 0 0 1rem;
}
.page.entry .sec h3 { font-size: 1.375rem; color: var(--e-tagline); margin: 1.75rem 0 .75rem; }
.page.entry .sec p, .page.entry .sec li {
  font-size: 1.25rem; line-height: 1.7; color: var(--e-body); max-width: none;
}
.page.entry .sec p { margin: 0 0 1rem; }
.page.entry .sec ul, .page.entry .sec ol { max-width: none; margin: 0 0 1rem; }
.page.entry .sec b, .page.entry .sec strong { color: var(--e-bone); font-weight: 600; }
.page.entry .pull { border-left-color: rgb(var(--e-verdigris)); }
.page.entry .pull p { color: var(--e-tagline); }

/* ── two link treatments, two behaviours ────────────────────────────────── */
/* Brass and dotted: a defined term, and hovering gives you the definition.
   Verdigris: this navigates. They used to be the same thing in two colours,
   and one of them came out browser-purple. */
/* Element-qualified, because a defined term is usually <b data-term> and
   `.page.entry .sec b` sets bone at the same specificity. Without the element
   in the selector the terms lose the tie and come out as ordinary bold. */
.page.entry [data-term],
.page.entry b[data-term],
.page.entry strong[data-term],
.page.entry span[data-term] {
  color: var(--e-brass); border-bottom: 1px dotted rgb(201 169 106 / .55);
  cursor: help; font-weight: 400;
}
.page.entry [data-term]:hover { border-bottom-color: var(--e-brass); }
/* Scoped to the prose container rather than excluded by :not(). A chain of
   three :not()s carries the specificity of everything inside it, which then
   outranks any later rule trying to make an exception — the Record's entry
   titles came out verdigris and could not be overridden. */
.page.entry .page-body a {
  color: var(--e-verd-lit); text-decoration: none;
  border-bottom: 1px solid rgb(var(--e-verdigris) / .4);
}
.page.entry .page-body a:visited { color: var(--e-verd-lit); }  /* never purple */
.page.entry .page-body a:hover { border-bottom-color: rgb(var(--e-verdigris)); }

/* A heading that is a link is still a heading. Verdigris marks a link inside
   prose, where it must be told apart from the text around it; at 2rem down a
   list of titles it just turns the page green. */
.page.entry .page-body h2 a,
.page.entry .page-body h2 a:visited { color: var(--e-bone); border-bottom: 0; }
.page.entry .page-body h2 a:hover { color: var(--e-verd-lit); }

/* The handoff cards carry their own border; they do not want an underline.
   RESTATE that border rather than zeroing the side. `border-bottom: 0` did
   remove the prose underline, and it removed the bottom of the card's own box
   with it, so both cards sat open at the foot of every page that has a pair.
   Three sides read as a rendering fault, which is how it was reported.
   The hover follows `.seq a:hover`, or the bottom edge would go full verdigris
   while the other three went to .6 of it. */
.page.entry .page-body .seq a { border-bottom: 1px solid rgb(var(--e-rule) / .09); }
.page.entry .page-body .seq a:hover { border-bottom-color: rgb(var(--e-verdigris) / .6); }

/* ── the table teaches the palette ──────────────────────────────────────── */
/* Numbers were proportional and left-aligned, so 20/30/40 did not line up as a
   series. Tabular and right-aligned, and each column takes the colour of what
   it means. */
.page.entry .tbl { max-width: none; font-size: 1.1875rem; }
.page.entry .tbl th {
  font-family: var(--font-mono); font-size: .6875rem; letter-spacing: .16em;
  color: var(--e-label); padding: 0 .9rem .625rem 0;
}
.page.entry .tbl td {
  padding: .8125rem .9rem .8125rem 0;
  border-top: 1px solid rgb(var(--e-rule) / .1);
  border-bottom: 1px solid rgb(var(--e-rule) / .06);
  color: var(--e-body);
}
.page.entry .tbl td:first-child { color: var(--e-bone); }
.page.entry .tbl .num {
  text-align: right; font-variant-numeric: tabular-nums lining-nums;
  color: var(--e-pewter);
}
.page.entry .tbl th.num { text-align: right; }
.page.entry .tbl .num--strike { color: #C8887A; }   /* what it does to you */
.page.entry .tbl .num--keep   { color: var(--e-brass); }  /* what you keep */

/* ── handing off ────────────────────────────────────────────────────────── */
/* Sequential rules need sequential navigation. The page used to end in a
   footer with two stray links. */
.seq { display: flex; align-items: stretch; gap: 1rem; margin-top: 3rem;
       padding-top: 1.75rem; border-top: 1px solid rgb(var(--e-rule) / .08); }
.seq > span { flex: 1; }   /* holds the half when there is no previous */
.seq a {
  flex: 1; display: flex; flex-direction: column; gap: .3125rem;
  padding: 1rem 1.25rem; text-decoration: none;
  border: 1px solid rgb(var(--e-rule) / .09); border-radius: 3px;
}
.seq a:hover { border-color: rgb(var(--e-verdigris) / .6); }
.seq__dir { font-family: var(--font-mono); font-size: .625rem; letter-spacing: .2em;
            text-transform: uppercase; color: var(--e-quiet); }
.seq__t { font-family: var(--font-display); font-size: 1.125rem; color: var(--e-bone); }
.seq a:last-child { text-align: right; }

.page.entry .foot {
  /* margin-top only. The shorthand set margin-inline to zero, which outranked
     the list page's `margin-inline: auto` and left the footer alone in running
     the full width while everything above it was inset. */
  margin-top: 3rem; padding: 1.5rem 0 3rem;
  border-top: 1px solid rgb(var(--e-rule) / .08);
  font-size: .8125rem; color: var(--e-legal);
}

/* ── narrow ─────────────────────────────────────────────────────────────── */
@media (max-width: 60rem) {
  .page.entry.page--spined { display: block; }
  /* Collapsed, not deleted: the rail is still the fastest way to reach a rule
     on a phone, it just sits above the prose instead of beside it. */
  .spine {
    position: static; max-height: none; border-right: 0;
    border-bottom: 1px solid rgb(var(--e-rule) / .08);
    padding: 1.5rem 1.25rem;
  }
  .spine__list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .page-body { padding: 1.5rem 1.25rem 2.5rem; }
  .topbar { padding: .75rem 1.25rem; gap: 1rem; }
  .topbar__mark { width: 7.5rem; }
  /* The one table in the app with five columns -- Tier / Rank / Dominion /
     Strike / Worth in hand, on /tomes -- has a minimum content width of 384px,
     and a 390px phone leaves 348px inside the page padding. So the table
     pushed 15px past the edge and took the whole page into horizontal scroll
     with it. Tightening the cells brings it to 348 exactly.

     SCOPED TO `.page.entry`, and that is not decoration: `.page.entry .tbl th`
     already exists in this file, so the obvious `.tbl th { padding-right }`
     loses to it and does nothing at all. It looks like it works, because the
     table is still the wrong width for a completely different reason. Assert
     the computed value, not just the page width.

     Not `table-layout: fixed`, which also fits: that forces equal columns on
     every table in the app to fix the one that overflows. */
  .page.entry .tbl th,
  .page.entry .tbl td { padding-right: .45rem; }
  .page.entry .tbl th { letter-spacing: .06em; }

  /* Beginning a Cycle is the one thing worth a phone's bar width. Contact is
     one tap away in the drawer, which is open by then anyway. */
  .topbar__acts { gap: .5rem; }
  .tbtn--quiet { display: none; }
  .page.entry .sec h2 { font-size: 1.875rem; }
  .page.entry .lede { font-size: 1.125rem; }
  .page.entry .sec p, .page.entry .sec li { font-size: 1.0625rem; }
  .seq { flex-direction: column; }
  .seq a:last-child { text-align: left; }
}

/* Very narrow. The bar is the mark, the menu button and the one action, and
   at 320px they came to 317px inside 277px of room -- so `Begin Cycle` pushed
   every content page into horizontal scroll. That was a regression from adding
   the button: it was checked at 390 and up, and 320 was never looked at.
   Nothing here changes above 368px, where the bar already fits. */
@media (max-width: 23rem) {
  .topbar { padding-left: .75rem; padding-right: .75rem; gap: .6rem; }
  .topbar__mark { width: 6.25rem; }
  .tbtn { padding: .45rem .7rem; font-size: .85rem; }
  /* The five-column table needs a second squeeze down here; .45rem clears a
     390px phone but not a 320px one. */
  .page.entry .tbl { font-size: .8rem; }
  .page.entry .tbl th,
  .page.entry .tbl td { padding-right: .25rem; }
  .page.entry .tbl th { font-size: .5rem; letter-spacing: .02em; }
  /* A worked example is a row of real cards. It WRAPS rather than shrinking,
     because `--card-md` deliberately does not shrink on narrow -- at this size
     the suit pip lands near 8px, which is where a misread becomes a misplay,
     and a teaching page is the worst place for that. */
  /* The row that HOLDS the cards is what must wrap; `.demo__cards` itself is
     `flex: none` inside `.demo`, so wrapping it alone changed nothing. */
  .demo { flex-wrap: wrap; }
  /* Wrapping alone did nothing: `.demo__cards` is `flex: none`, so it took its
     max-content width and was never narrow enough for the wrap to engage. It
     has to be allowed to SHRINK before it will wrap. */
  .demo__cards { flex: 1 1 auto; min-width: 0; flex-wrap: wrap; }
}

/* The drawer button lives in the bar. On a wide screen the bar lists every
   page itself, so the button would be a second door to the same room; on a
   narrow one those links are hidden and this is the only way across the site. */
/* Shown at every width, not just below 60rem. It was the only navigation on a
   phone and invisible on a desktop, so the one control people go looking for
   was the one control that moved. Keeping it also pushes the bar's links clear
   of the spine, which they used to overlap. */
.topbar .pagenav__btn { display: grid; }
@media (max-width: 60rem) {
  /* In flow on a phone: no spine beside the content to align to, so the
     button leads the bar and the wordmark follows. A tap area, no box. */
  .topbar .pagenav__btn { display: grid; position: static; transform: none;
    min-width: 2.55rem; min-height: 2.55rem; place-items: center; margin-right: .25rem; }
}

/* ── a list page ───────────────────────────────────────────────────────────
   The Record is a list, not prose, so the 68-character measure that suits a
   paragraph starves it: the rows were 760px hard against the left edge with
   600px of nothing beside them. It gets 80% of its column, centred, so the
   separators are inset from both sides and read as rules rather than as the
   edge of the page. */
.page-body--wide { max-width: none; }
.page-body--wide > * { width: 80%; margin-inline: auto; }


/* The filters live in the rail now, styled as what they are: navigation. */
.spine__list .rec-cat {
  display: flex; gap: .75rem; align-items: baseline; width: 100%;
  padding: .5rem 0 .5rem .75rem; text-align: left; cursor: pointer;
  background: none; border: 0; border-left: 2px solid transparent;
  font-family: var(--font-display);
}
/* The button was a chip before it was navigation; without this it keeps the
   chip's caps and tracking and reads differently from every other rail. */
.spine__list .rec-cat .spine__t {
  text-transform: none; letter-spacing: 0; font-size: 1rem;
}
.spine__list .rec-cat:hover .spine__t { color: var(--e-bone); }
.spine__list .rec-cat.is-on { border-left-color: rgb(var(--e-verdigris));
                              background: rgb(var(--e-verdigris) / .07); }
.spine__list .rec-cat.is-on .spine__t { color: var(--e-bone); }
.spine__list .rec-cat.is-on .spine__n { color: var(--e-verd-lit); }

@media (max-width: 60rem) {
  .page-body--wide > * { width: 100%; }
}


/* 1 · the labels this sweep had missed. Same rule as the board: a word that
   names a thing is chrome, and chrome is mono. */
.gloss__kind, .eyebrow, .rel__cat, .rel__when,
.entry-meta, .entry-by, .entry-nav__dir {
  font-family: var(--font-mono);
}

/* 7 · "Read this entry" is the only action on a Chronicle row and it was set
   smaller than the summary above it. */
.rel__more { font-size: .86rem; letter-spacing: .02em; }

/* .demo__sum is normally the arithmetic of a worked example -- "1 + 8 = 9" --
   and a numeral stays in the serif. These two are verdicts, not sums, so they
   take the label treatment instead. */
.demo__sum--verdict {
  font-family: var(--font-mono); font-size: .72rem; letter-spacing: .18em;
  text-transform: uppercase;
}

/* 4 · the bar's links clear the spine on their own once the menu button is
   there: the button plus the mark come to the rail's width, so the first link
   lands just past it instead of floating over it. No explicit width — forcing
   one only padded the mark with dead space. */

/* 2 · the Chronicle's rows take the same measure as every other page's prose,
   so the two land on the same left edge. They were 80% of a wider column,
   which put them 29px further left than the pages either side of them. */
.page-body--wide > * { width: 100%; max-width: 44.7rem; margin-inline: auto; }

/* 3 · A table on these pages is a panel, and a panel needs a gutter. The
   cells had no left padding at all, so the first column — Blood, Dream,
   Swarm, Void — sat directly on the panel's edge. */
.page.entry .tbl th:first-child,
.page.entry .tbl td:first-child { padding-left: 1.1rem; }
.page.entry .tbl th:last-child,
.page.entry .tbl td:last-child { padding-right: 1.1rem; }
.page.entry .tbl thead th { padding-top: .9rem; }
.page.entry .tbl tbody tr:last-child td { padding-bottom: .9rem; }


/* ── the Pantheon ───────────────────────────────────────────────────────────
 * Twelve entities and their four hidden siblings. The art is injected from
 * window.ROYALS, so these cells hold whatever the deck currently draws. */
.ents { display: grid; gap: 1.1rem; margin: 1.4rem 0 0; }
/* minmax(0, 1fr), never a bare 1fr. A `1fr` track has an AUTO minimum, so an
   entity cell -- a card beside a stats block -- sets a floor the column cannot
   go below, and the whole page-body inflates to fit it. At 1024px that pushed
   /pantheon 150px past the viewport while every other page was fine, because
   only this page has such a cell. `.page.entry.page--spined` already guards
   its own tracks the same way; this one was missed. */
/* THE GALLERY BREAKS OUT OF THE READING MEASURE. `.page-body` is capped at
   44.7rem because that is ~68 characters, which is right for prose and wrong
   for a bestiary of card art: two columns inside it are 371px and the card is
   138px inside that. So the grid -- and only the grid -- bleeds right into the
   slack the left-aligned column leaves. The prose above it keeps its measure.

   The bleed is CLAMPED rather than fixed, because the slack depends on the
   viewport: 272px at 1440, none at 1152. A fixed -14rem looked right on a wide
   screen and overflowed by 102px on a narrower one. `clamp()` takes whatever
   is going and stops at zero.

   `minmax(23rem, …)` is the largest minimum that still gives two columns at
   1280px; at 24rem it drops to one 816px cell holding a 180px card. */
.ents {
  margin-right: clamp(-14rem, calc(72rem - 100vw), 0rem);
  /* `min(23rem, 100%)`, never a bare 23rem: a minmax minimum is a FLOOR the
     track cannot go below, so on a 390px phone the cell became 391px and the
     page scrolled sideways. Wrapping it in min() lets the floor collapse to
     the container when the container is the smaller of the two. */
  grid-template-columns: repeat(auto-fit, minmax(min(23rem, 100%), 1fr));
}
/* 10rem is a desktop size. On a phone the cell is 333px and a 180px card
   overflowed it by 37px, so narrow keeps the size it always had. */
.ent__card { --card-xl: 10rem; }
@media (max-width: 46rem) { .ent__card { --card-xl: 7.5rem; } }

/* Two columns only once there is room for two, and `minmax(0, 1fr)` rather
   than a bare `1fr`. Both halves matter and each fixes a different fault.

   A `1fr` track has an AUTO minimum, so an entity cell -- a card beside a
   stats block -- set a floor the column could not go below and the whole
   page-body inflated to fit it. At 1024px that pushed /pantheon 150px past the
   viewport while every other page was fine, because only this page has such a
   cell. `.page.entry.page--spined` already guards its own tracks this way;
   this one was missed.

   Guarding the track then revealed what the overflow had been hiding: two
   columns inside a spined layout at 1024px are 265px each, and the epithet
   wrapped to four lines in 71px. The page had been growing wider instead of
   admitting it did not fit. 72rem is where two columns genuinely fit. */
/* No breakpoint any more: auto-fit decides how many columns fit, which is what
   the media query was approximating. Kept as a note because the reasoning
   below is what stops a bare `1fr` coming back. */

.ent {
  display: flex; gap: 1rem; align-items: flex-start;
  padding: 1rem 1.1rem;
  border: 1px solid rgb(var(--e-rule) / .1);
  border-radius: .6rem;
  background: rgb(var(--ui-800-rgb) / .55);
}
.ent__art {
  flex: none; width: 5.5rem; aspect-ratio: 1;
  color: var(--e-bone, rgb(var(--ui-text-100-rgb)));
  --card-bg: rgb(var(--ui-900-rgb));
}
.ent__art svg { display: block; width: 100%; height: 100%; }
.ent.is-artless .ent__art { display: none; }
.ent__id { min-width: 0; }
.ent__name { font-family: var(--font-display); font-size: 1.4rem; font-weight: 600;
             margin: 0; color: var(--e-bone, rgb(var(--ui-text-100-rgb))); }
.ent__epithet {
  margin: .15rem 0 0; font-family: var(--font-mono);
  font-size: .6rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--e-label, rgb(var(--ui-text-600-rgb)));
}
.ent__stats { display: flex; flex-wrap: wrap; gap: .2rem 1.1rem; margin: .7rem 0 0; }
.ent__stats div { display: flex; gap: .4rem; align-items: baseline; }
.ent__stats dt {
  font-family: var(--font-mono); font-size: .55rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--e-label, rgb(var(--ui-text-600-rgb)));
}
.ent__stats dd { margin: 0; font-size: .95rem; color: var(--e-body, rgb(var(--ui-text-300-rgb))); }
.ent__stats dd.num { font-variant-numeric: tabular-nums lining-nums; }
/* Carried for the lightbox to read, not shown in the grid. A 10rem cell has
   no room for sixty words, and the overlay reads its fields off this article
   rather than off a second data structure, so the text has to BE here. */
.ent__dread, .ent__about { display: none; }
.ent__note { margin: .6rem 0 0; font-size: .85rem; line-height: 1.55;
             color: var(--e-body, rgb(var(--ui-text-400-rgb))); }
.ent__note b { color: var(--e-bone, rgb(var(--ui-text-200-rgb))); }

/* The campaign boss is a spoiler for anyone who has not reached one, so it is
   set apart rather than mixed in with the twelve. */
.sec--spoiler { border-top: 1px solid rgb(var(--e-rule) / .12); margin-top: 3rem; padding-top: 2.4rem; }
.ent--boss .ent__art { color: var(--e-brass, rgb(var(--ui-text-200-rgb))); }

/* ── figures inside a Chronicle entry ───────────────────────────────────────
 * Rendered from the live deck and the live Pantheon art, never from an image,
 * so an entry cannot show a card the game would draw differently and nothing
 * here goes stale when the deck is reskinned. */
.fig { margin: 1.8rem 0; }
.fig__cards { display: flex; flex-wrap: wrap; gap: .5rem; }
.fig__royal {
  display: flex; flex-direction: column; align-items: center; gap: .5rem;
  width: 7rem; color: var(--e-bone, rgb(var(--ui-text-100-rgb)));
  --card-bg: rgb(var(--ui-900-rgb));
}
.fig__royal svg { display: block; width: 100%; height: auto; }
.fig__row { display: flex; flex-wrap: wrap; gap: 1.6rem; align-items: flex-end; }
.fig__cap {
  font-family: var(--font-mono); font-size: .55rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--e-label, rgb(var(--ui-text-600-rgb)));
}
.fig__note {
  margin: .7rem 0 0; font-family: var(--font-mono); font-size: .6rem;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--e-label, rgb(var(--ui-text-600-rgb)));
}

/* A table written in Markdown gets no class, so the Chronicle's tables were
   arriving unstyled next to the hand-authored .tbl ones. Same treatment. */
.entry-body table {
  width: 100%; border-collapse: collapse; margin: 1.6rem 0;
  background: rgb(var(--ui-800-rgb) / .55);
  border-radius: .5rem; overflow: hidden;
}
.entry-body th {
  text-align: left; font-family: var(--font-mono);
  font-size: .6875rem; letter-spacing: .16em; text-transform: uppercase;
  font-weight: 400; color: var(--e-label, rgb(var(--ui-text-600-rgb)));
  padding: .9rem 1.1rem .6rem;
}
.entry-body td {
  padding: .7rem 1.1rem;
  border-top: 1px solid rgb(var(--e-rule) / .1);
  color: var(--e-body, rgb(var(--ui-text-300-rgb)));
}
.entry-body td:first-child { color: var(--e-bone, rgb(var(--ui-text-100-rgb))); }


/* ── The Pantheon lightbox ───────────────────────────────────────────────────
   The grid draws an entity at 10rem. This draws it at whatever the window
   allows, which is the only place the painted masters are shown at anything
   like their own resolution.

   The art itself is painted by `static/card-art.css`, which is GENERATED --
   the mask URLs and which entities own a detail crop both come out of
   `design/build-card-art.py`, so art and rules cannot drift. Nothing here
   names an image. */
.lb[hidden] { display: none; }
.lb {
  position: fixed; inset: 0; z-index: 90;
  display: flex; align-items: center; justify-content: center;
  padding: clamp(.75rem, 3vw, 2.5rem);
}
.lb__scrim {
  position: absolute; inset: 0;
  background: rgb(0 0 0 / .82);
  backdrop-filter: blur(2px);
}
body.is-lb { overflow: hidden; }

.lb__box {
  position: relative;
  display: grid; gap: clamp(1rem, 3vw, 2.2rem);
  /* minmax(0, …) on both, or the art's intrinsic size sets a floor the box
     cannot go below and the dialog outgrows a narrow window. The same fault
     `.ents` and the report dialog each had. */
  grid-template-columns: minmax(0, 20rem) minmax(0, 26rem);
  align-items: center;
  max-width: min(56rem, 100%); max-height: 100%;
  overflow: auto;
  padding: clamp(1.1rem, 3vw, 2rem);
  border: 1px solid rgb(var(--ui-text-600-rgb) / .28);
  border-radius: .8rem;
  background: rgb(var(--ui-900-rgb));
}
.lb__box:focus { outline: none; }
.lb__box:focus-visible { outline: 2px solid rgb(var(--ui-accent-rgb) / .5); }

.lb__figure { position: relative; display: flex; flex-direction: column;
              align-items: center; gap: .8rem; }

/* Painted exactly as a card is: the skin's ink through the master's own alpha.
   An <img> could not follow the skin, which is why this is a mask. */
.lb__art {
  position: relative;
  width: 100%; aspect-ratio: 0.689;
  color: var(--art-ink, rgb(var(--ui-text-100-rgb)));
  background-color: currentColor;
  -webkit-mask-size: contain; mask-size: contain;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  transition: -webkit-mask-image .18s ease, mask-image .18s ease;
}
/* An entity with no painted master shows its drawing, which is a vector and
   scales better than any raster would. It is not a lesser case. */
/* The luminance multiply, exactly as a card does it. Without this the
   lightbox is currentColor through the alpha and nothing else, which is a
   FLATTER rendering of the same painting: the modelling that the master's own
   brightness carries would simply be missing, and the overlay would disagree
   with the card behind it about what the creature looks like. The per-card
   image comes from the generated stylesheet; nothing here names a file. */
.lb__art::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-position: center; background-size: contain;
  background-repeat: no-repeat;
  mix-blend-mode: multiply;
}
.lb__art.is-vector::after { content: none; }
.lb__art.is-vector { background: none; -webkit-mask-image: none; mask-image: none; }
.lb__art.is-vector svg { width: 100%; height: 100%;
                         color: var(--art-ink, rgb(var(--ui-text-100-rgb))); }

/* Shown only for entities that HAVE a detail crop, and the generated
   stylesheet is what turns it on. No list of card ids lives here. */
.lb__zoom { display: none; }
.lb__zoom, .lb__step {
  align-items: center; gap: .4rem;
  font-family: var(--font-mono); font-size: .66rem;
  letter-spacing: .14em; text-transform: uppercase;
  padding: .5rem .9rem;
  color: rgb(var(--ui-text-200-rgb));
  background: none;
  border: 1px solid rgb(var(--ui-text-600-rgb) / .4);
  border-radius: .4rem; cursor: pointer;
}
.lb__zoom:hover, .lb__step:hover {
  color: rgb(var(--ui-accent-rgb));
  border-color: rgb(var(--ui-accent-rgb) / .6);
}

.lb__tier { font-family: var(--font-mono); font-size: .62rem;
            letter-spacing: .2em; text-transform: uppercase;
            color: rgb(var(--ui-text-600-rgb)); margin: 0; }
.lb__name { font-family: var(--font-display); font-weight: 600;
            font-size: clamp(1.8rem, 4vw, 2.6rem); line-height: 1;
            letter-spacing: -.02em; margin: .4rem 0 .3rem; }
.lb__epithet { font-family: var(--font-display); font-style: italic;
               font-size: 1.05rem; color: rgb(var(--ui-text-300-rgb));
               margin: 0 0 1rem; }
.lb__stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
             gap: .6rem 1rem; margin: 0 0 1rem; }
.lb__stats dt { font-family: var(--font-mono); font-size: .58rem;
                letter-spacing: .16em; text-transform: uppercase;
                color: rgb(var(--ui-text-600-rgb)); }
.lb__stats dd { margin: .15rem 0 0; font-family: var(--font-display);
                font-size: 1.15rem; color: rgb(var(--ui-text-100-rgb)); }
/* The one word for what KIND of dread this one is. It sits with the tier
   because both answer "what am I looking at" before any number does. */
.lb__dread { display: flex; align-items: baseline; gap: .5rem;
             margin: .35rem 0 0; font-family: var(--font-display);
             font-size: 1rem; color: rgb(var(--ui-text-200-rgb)); }
.lb__dreadlab { font-family: var(--font-mono); font-size: .58rem;
                letter-spacing: .16em; text-transform: uppercase;
                color: rgb(var(--ui-text-600-rgb)); }
.lb__dread[hidden] { display: none; }

/* Sixty words on what the thing is. Above the rule note, because what it IS
   comes before what it does to your hand. */
.lb__about { font-size: .95rem; line-height: 1.62;
             color: rgb(var(--ui-text-200-rgb)); margin: 0 0 .9rem; }
.lb__about[hidden] { display: none; }
.lb__note { font-size: .92rem; line-height: 1.55;
            color: rgb(var(--ui-text-300-rgb)); margin: 0; }

.lb__nav { grid-column: 1 / -1;
           display: flex; align-items: center; justify-content: space-between;
           gap: 1rem; padding-top: .4rem;
           border-top: 1px solid rgb(var(--ui-text-600-rgb) / .28); }
.lb__step { display: inline-flex; }
.lb__count { font-family: var(--font-mono); font-size: .62rem;
             letter-spacing: .16em; color: rgb(var(--ui-text-600-rgb));
             font-variant-numeric: lining-nums tabular-nums; }

.lb__x {
  position: absolute; top: .5rem; right: .6rem;
  width: 2.2rem; height: 2.2rem; line-height: 1;
  font-size: 1.5rem; color: rgb(var(--ui-text-500-rgb));
  background: none; border: 0; cursor: pointer; border-radius: .3rem;
}
.lb__x:hover { color: rgb(var(--ui-accent-rgb)); }

/* The card cell becomes a control. The button covers the card rather than the
   whole article, so the stats beside it stay selectable text. */
.ent__card { position: relative; }
.ent__open {
  position: absolute; inset: 0;
  background: none; border: 0; padding: 0; cursor: pointer;
  border-radius: .5rem;
}
.ent__open:hover { box-shadow: 0 0 0 2px rgb(var(--ui-accent-rgb) / .45); }
.ent__open:focus-visible { outline: 2px solid rgb(var(--ui-accent-rgb));
                           outline-offset: 2px; }

@media (max-width: 46rem) {
  .lb__box { grid-template-columns: minmax(0, 1fr); }
  /* An EXPLICIT width, not `max-width` plus `margin-inline: auto`. That pair
     makes a grid item shrink-to-fit -- auto margins override `justify-self:
     stretch` -- and `.lb__art`'s `width: 100%` then resolves against a
     shrink-to-fit parent, which is circular. It collapsed to 82px: smaller
     than the 7.5rem card in the grid this overlay exists to enlarge. */
  .lb__figure { width: min(20rem, 82vw); margin-inline: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .lb__art { transition: none; }
}

/* ── GAZING ────────────────────────────────────────────────────────────── */
.gz { display: flex; flex-direction: column; gap: .4rem; margin-top: 1rem; }
.gz__row {
  display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1.2fr) auto auto;
  gap: 1rem; align-items: center; text-decoration: none;
  padding: .8rem 1rem; border: 1px solid rgb(var(--ui-text-600-rgb) / .28);
  border-radius: .5rem; transition: border-color .12s ease, background .12s ease;
}
.gz__row:hover { border-color: rgb(var(--ui-accent-rgb) / .6); background: rgb(var(--ui-800-rgb)); }
.gz__who, .gz__mid, .gz__num, .gz__tail { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.gz__tail { align-items: flex-end; }
.gz__seats { font-family: var(--font-display); font-size: 1.05rem; color: rgb(var(--ui-text-100-rgb)); }
/* The name and its tier are two lines, not one run-on: `.gz__mid` is the
   column but `.gz__enemy` holds both, so it has to stack them itself or
   "NightgauntSPAWN · J♠" is what renders. */
.gz__enemy { display: flex; flex-direction: column; gap: .1rem;
             font-family: var(--font-display); font-size: 1rem;
             color: rgb(var(--ui-text-200-rgb)); }
.gz__mode, .gz__tier, .gz__stat, .gz__turns, .gz__gazers, .gz__live {
  font-family: var(--font-mono); font-size: .58rem; letter-spacing: .14em;
  text-transform: uppercase; color: rgb(var(--ui-text-400-rgb));
}
.gz__stat { color: rgb(var(--ui-text-300-rgb)); }
.gz__stat--dim { color: rgb(var(--ui-text-400-rgb)); }
/* Live takes the accent, because it is the one row you can still affect by
   turning up. Everything else on this page has already happened. */
.gz__live { color: rgb(var(--ui-accent-rgb)); }
.gz__wait, .gz__say { color: rgb(var(--ui-text-400-rgb)); font-size: .95rem; }
@media (max-width: 46rem) {
  .gz__row { grid-template-columns: minmax(0, 1fr) auto; row-gap: .5rem; }
  .gz__tail { grid-column: 2; }
}
/* A dreamt descent can be retold, and the row says so rather than making a
   reader discover it by opening one. */
.gz__retell {
  font-family: var(--font-mono); font-size: .58rem; letter-spacing: .14em;
  text-transform: uppercase; color: rgb(var(--ui-accent-rgb));
}


/* THE WRITING INDEX. A short editorial list of longer pieces, distinct from the
   Chronicle's dated stubs: a title, a date, and a sentence. Reachable only from
   writing.html, which is why it lives here rather than in the game's deck.css. */
.writ { list-style: none; margin: 2rem 0 0; padding: 0; }
.writ__item { padding: 1.4rem 0; border-top: 1px solid rgb(var(--e-rule) / .14); }
.writ__item:first-child { border-top: 0; }
.writ__link { display: flex; justify-content: space-between; align-items: baseline;
  gap: 1rem; flex-wrap: wrap; text-decoration: none; }
.writ__title { font-family: var(--font-display); font-weight: 600; font-size: 1.5rem;
  color: var(--e-bone); letter-spacing: -.01em; }
.writ__link:hover .writ__title { color: rgb(var(--e-verdigris)); }
.writ__date { font-family: var(--font-mono); font-size: .62rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--e-muted, var(--e-body)); white-space: nowrap; }
.writ__sub { margin: .5rem 0 0; color: var(--e-body); font-size: 1.02rem; line-height: 1.55; }
