@import url(style_cs.1792802385.css);

* {
  box-sizing: border-box;
}

/* every font-size below this point is in rem, relative to this one root
   value — bump --font-size-base in colors.css to rescale the whole forum */
html {
  font-size: var(--font-size-base);
}

/* screen-reader-only utility — used throughout the forum markup (post author
   labels, action link suffixes) to keep text accessible without showing it */
.acchide,
/* "(Íåò àâàòàðà)" fallback text — the placeholder box below replaces it visually */
#viewprofile-next.no-avatar #pa-avatar strong,
/* lore-article tab radios: same clip technique, kept clickable via their
   <label for>, see LORE ARTICLE TEMPLATE below (the timeline's era radios
   carry .lore-tab-input too); chronicle-filter-input is the timeline's
   scope-filter checkboxes, same technique, kept clickable via their own
   <label for>. */
.lore-article .lore-tab-input,
.chronicle-page .chronicle-filter-input {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* form-element resets .acchide doesn't need (it's never applied to an <input>) */
.lore-article .lore-tab-input,
.chronicle-page .chronicle-filter-input {
  margin: calc(-0.125 * var(--base-spacing));
  padding: 0;
  border: 0;
}

html,
body {
  margin: 0;
  padding: 0;
  background-color: var(--color-bg);
  /* subtle SVG feTurbulence grain so the dark page field isn't a flat fill —
     kept as a tiny tiled data URI instead of an uploaded file (upload_file
     silently serves stale content on re-upload under the same name) */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.05 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-repeat: repeat;
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  font-kerning: normal;
}

/* base link style, wherever a more specific component rule doesn't already
   set one (nav, cards, dark-surface menus etc. all style their own anchors) */
a {
  color: var(--color-link);
  text-decoration: none;
}

a:hover {
  color: var(--color-heading-hover);
}

#html-header {
  width: 100%;
  max-width: 1082px;
  margin: 0 auto;
  position: relative;
}

/* ---- HERO ---- */
.hero {
  position: relative;
  width: 100%;
  height: 390px;
  overflow: hidden;
  border-bottom: 2px solid var(--color-border);
  background: url("https://upforme.ru/uploads/001c/c7/56/2/844766.jpg") no-repeat;
  background-size: cover;
  background-position: center top;
}

.hero img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: saturate(1.05);
}

.hero::after {
  /* gentle vignette so overlaid text stays legible on either half */
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(var(--color-bg-rgb), 0.55) 0%, rgba(var(--color-bg-rgb), 0.05) 30%, rgba(var(--color-bg-rgb), 0.05) 70%, rgba(var(--color-bg-rgb), 0.6) 100%);
}

.title-overlay {
  position: absolute;
  top: calc(2.5 * var(--base-spacing));
  left: 0;
  right: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}

.title-overlay h1 {
  margin: 0;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 2.71429rem;
  letter-spacing: 5px;
  color: var(--color-text-on-dark);
  line-height: 1;
  text-shadow: var(--text-shadow-hero);
}

.title-overlay .subtitle {
  margin-top: calc(1 * var(--base-spacing));
  font-style: italic;
  font-size: 1.14286rem;
  letter-spacing: 2px;
  color: var(--color-accent-light);
  line-height: 1;
  text-shadow: 0 1px 6px rgba(var(--color-shadow-rgb), 0.85);
}

.title-meta {
  margin-top: calc(1 * var(--base-spacing));
  font-family: var(--font-heading);
  font-size: 0.78571rem;
  letter-spacing: 3px;
  text-transform: uppercase;
  color: var(--color-text-cream);
  line-height: 1;
  text-shadow: 0 1px 4px rgba(var(--color-shadow-rgb), 0.95), 0 0 12px rgba(var(--color-shadow-rgb), 0.7);
}

.title-meta-sep {
  margin: 0 calc(1 * var(--base-spacing));
  font-size: 0.6em;
  vertical-align: middle;
  color: var(--color-accent);
}

/* ---- HIGHLIGHTS STRIP (overlaid on hero) ---- */
.highlights {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 3;
  display: flex;
  background: rgba(var(--color-bg-rgb), 0.6);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  border-top: 1px solid rgba(var(--color-accent-rgb), 0.4);
}

/* wraps the first 3 cards so mobile can turn them into a carousel with nav
   arrows and dots (see MOBILE below) without touching the desktop layout —
   display:contents on the wrapper and its inner viewport/track unwraps them
   invisibly into the flex algorithm above 640px; the nav/dots controls are
   mobile-only UI so they're simply hidden here rather than unwrapped */
.highlights-carousel,
.highlights-carousel-viewport,
.highlights-carousel-track {
  display: contents;
}

.carousel-nav,
.carousel-dots {
  display: none;
}

.highlight-card {
  flex: 1;
  padding: calc(1.25 * var(--base-spacing)) calc(2 * var(--base-spacing)) calc(1.5 * var(--base-spacing));
  border-right: 1px solid rgba(var(--color-accent-rgb), 0.2);
}

.highlight-card--wide {
  flex: 1.3;
}

.highlight-card--queue {
  flex: 1.15;
  border-right: none;
}

.highlight-label {
  font-family: var(--font-heading);
  font-size: 0.78571rem;
  letter-spacing: 2px;
  color: var(--color-accent);
  margin-bottom: calc(1.25 * var(--base-spacing));
  display: block;
  text-transform: uppercase;
}

/* players */
.players-row {
  display: flex;
  gap: calc(1.25 * var(--base-spacing));
}

.player {
  text-align: center;
  flex: 1;
}

.player .av {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  margin: 0 auto calc(0.5 * var(--base-spacing));
  background: linear-gradient(145deg, var(--color-text-muted), var(--color-text));
  border: 2px solid var(--color-accent);
}

.player .name {
  font-size: 0.71429rem;
  color: var(--color-text-cream);
  line-height: 1.15;
}

/* best post / episode */
.post-title,
.episode-title {
  font-family: var(--font-heading);
  font-size: 0.92857rem;
  color: var(--color-text-on-dark);
  margin: 0 0 calc(0.375 * var(--base-spacing));
}

.post-excerpt,
.episode-desc {
  font-size: 0.85714rem;
  color: var(--color-text-on-dark-subtle);
  font-style: italic;
  line-height: 1.3;
  margin: 0 0 calc(0.5 * var(--base-spacing));
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.highlight-link {
  font-size: 0.85714rem;
  color: var(--color-accent-hover);
  text-decoration: none;
  border-bottom: 1px solid var(--color-border);
}

.highlight-link:hover {
  color: var(--color-text-on-dark);
}

/* ---- QUEST QUEUE (compact, inside overlay) ---- */
.quest-mini {
  list-style: none;
  margin: 0;
  padding: 0;
}

.quest-mini li {
  display: flex;
  flex-direction: column;
  padding: calc(0.375 * var(--base-spacing)) 0;
  border-bottom: 1px solid rgba(var(--color-accent-rgb), 0.15);
}

.quest-mini li:last-child {
  border-bottom: none;
}

.q-name {
  font-size: 0.82143rem;
  color: var(--color-text-on-dark);
  font-style: italic;
  line-height: 1.2;
}

.q-meta {
  font-size: 0.71429rem;
  color: var(--color-text-on-dark-subtle);
  letter-spacing: 0.2px;
}

/* ---- LAUNCH PROGRESS (test-run stand-in for the highlight cards) ----
   one number, --launch-progress (0-100, unitless, set inline in
   forms/header.html), drives both the fill width and the printed percent —
   the counter is the only way CSS can turn a custom property into text.
   A gold diamond rides the tip of the fill, echoing .title-meta-sep. */
.launch-card {
  border-right: none;
  counter-reset: launch-progress var(--launch-progress);
}

.launch-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: calc(2 * var(--base-spacing));
}

.launch-percent {
  font-family: var(--font-heading);
  font-size: 1.28571rem;
  letter-spacing: 1px;
  line-height: 1;
  color: var(--color-accent-light);
}

.launch-percent::after {
  content: counter(launch-progress) "%";
}

/* faint tick every 10% along the empty rail */
.launch-bar {
  position: relative;
  height: calc(0.75 * var(--base-spacing));
  margin: calc(0.5 * var(--base-spacing)) 0;
  border: 1px solid rgba(var(--color-accent-rgb), 0.35);
  background:
    repeating-linear-gradient(90deg, transparent 0 calc(10% - 1px), rgba(var(--color-accent-rgb), 0.2) calc(10% - 1px) 10%),
    rgba(var(--color-bg-rgb), 0.6);
}

.launch-bar::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(var(--launch-progress) * 1%);
  background: linear-gradient(90deg, var(--color-border), var(--color-accent-light));
  box-shadow: 0 0 var(--base-spacing) rgba(var(--color-accent-rgb), 0.55);
}

.launch-bar::after {
  content: "";
  position: absolute;
  top: 50%;
  left: calc(var(--launch-progress) * 1%);
  width: calc(1.75 * var(--base-spacing));
  height: calc(1.75 * var(--base-spacing));
  transform: translate(-50%, -50%) rotate(45deg);
  border: 1px solid var(--color-accent-bright);
  background: var(--color-accent);
  box-shadow: 0 0 var(--base-spacing) rgba(var(--color-accent-rgb), 0.7);
}

/* ---- NEWS STRIP (below hero) ----
   date seals + title + short note, one link per item. "safe center" keeps
   the row centered while it fits but falls back to start-aligned once it
   overflows, so the first item isn't clipped off the left edge on scroll. */
.news-strip {
  display: flex;
  justify-content: safe center;
  gap: calc(1.5 * var(--base-spacing));
  padding: calc(1.5 * var(--base-spacing)) calc(2 * var(--base-spacing));
  background: var(--color-surface-raised);
  border-bottom: 1px solid rgba(var(--color-accent-rgb), 0.3);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  /* match the side padding, or snapping parks the first item flush against the edge */
  scroll-padding-inline: calc(2 * var(--base-spacing));
  scrollbar-width: none;
}

.news-strip::-webkit-scrollbar {
  display: none;
}

.news-item {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: calc(1.25 * var(--base-spacing));
  padding: calc(0.625 * var(--base-spacing)) calc(2.5 * var(--base-spacing)) calc(0.625 * var(--base-spacing)) calc(0.625 * var(--base-spacing));
  border: 1px solid rgba(var(--color-accent-rgb), 0.25);
  border-radius: calc(4 * var(--base-spacing));
  scroll-snap-align: start;
  transition: border-color 0.2s, background-color 0.2s;
}

/* color: the global a:hover crimson is for parchment and would bleed into
   any pill text that isn't wrapped in its own colored span */
.news-item:hover,
.chronicle-chip:hover,
.gb-link:hover {
  border-color: rgba(var(--color-accent-rgb), 0.6);
  background: rgba(var(--color-accent-rgb), 0.06);
  color: var(--color-text-on-dark);
}

/* coin-like seal: solid gold rim plus a faint inner ring, drawn with inset
   shadows so the date numerals stay centered without extra markup */
.news-date {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: calc(5 * var(--base-spacing));
  height: calc(5 * var(--base-spacing));
  border: 1px solid var(--color-accent);
  border-radius: 50%;
  box-shadow: inset 0 0 0 2px var(--color-surface-raised), inset 0 0 0 3px rgba(var(--color-accent-rgb), 0.35);
  font-size: 0.78571rem;
  font-weight: 600;
  letter-spacing: 0.5px;
  color: var(--color-accent-light);
}

.news-text {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
}

.news-title {
  font-size: 0.92857rem;
  font-weight: 600;
  color: var(--color-text-on-dark);
}

.news-desc {
  font-size: 0.78571rem;
  font-style: italic;
  color: var(--color-text-on-dark-subtle);
}

/* keep our header block from inheriting forum body font defaults */
#html-header,
#html-header * {
  font-family: var(--font-body);
  box-sizing: border-box;
}

#html-header .title-overlay h1,
#html-header .highlight-label,
#html-header .news-date {
  font-family: var(--font-heading);
}

#pun {
  border-left: 1px solid var(--color-border);
  border-right: 1px solid var(--color-border);
  border-bottom: 2px solid var(--color-border);
  width: fit-content;
  margin: 0 auto;
  width: 1080px;
  max-width: 100%;
}

/* ============ PAGE-WIDE UNIFICATION ============ */
/* .punbb already wraps everything below the header in the real markup —
   turning it into one continuous parchment panel instead of per-section defaults */
.punbb {
  position: relative;
  display: flex;
  flex-direction: column;
  max-width: 1082px;
  margin: 0 auto;
  background-position: 0 -169px;
  background-color: var(--color-punbb-bg-fallback);
  box-shadow: 0 10px 34px rgba(var(--color-shadow-rgb), 0.5);
  overflow: hidden;
}

/* pull navlinks/ulinks above html-header visually — DOM order unchanged, only
   flex `order` reshuffles paint order; all other children stay at default
   order 0 so they keep their normal relative sequence */

/* strip the platform's default panel chrome so everything reads as one paper surface */
.punbb .section {
  border: none !important;
  box-shadow: none !important;
  margin: 0 !important;
  font-family: var(--font-body);
  color: var(--color-text);
  background: rgba(var(--color-bg-rgb), 0.93);
  backdrop-filter: blur(2px);
}

.punbb .crumbs {
  font-family: var(--font-heading);
  font-size: 0.78571rem;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--color-text-on-dark-muted);
  background: transparent;
  border: none;
  border-bottom: 1px dashed rgba(var(--color-border-rgb), 0.3);
  box-shadow: none;
  box-sizing: border-box;
  margin: 0;
  padding: calc(1 * var(--base-spacing)) calc(3 * var(--base-spacing));
}

/* "you are here" label reads as the anchor point, brighter than the trail around it */
.punbb .crumbs strong {
  display: none;
}

/* chevron separators sit quiet so the eye lands on labels and links, not dividers */
.punbb .crumbs em {
  color: rgba(var(--color-accent-rgb), 0.5);
  font-style: normal;
}

.punbb .crumbs a,
.punbb .crumbs span {
  color: var(--color-accent);
  text-decoration: none;
}

.punbb .crumbs a:hover {
  color: var(--color-accent-bright);
  text-decoration: underline;
}

.punbb #pun-main {
  background: var(--color-panel-bg-alt);
  padding: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}

.punbb #pun-main>h1 {
  grid-column: 1;
  grid-row: 1;
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(180deg, var(--color-banner-bg-light) 0%, var(--color-banner-bg) 100%);
  border-top: 2px solid var(--color-accent);
  border-bottom: 2px solid var(--color-accent);
  padding: calc(1.5 * var(--base-spacing)) calc(2.5 * var(--base-spacing));
  margin: 0;
  text-align: center;
  box-shadow: 0 2px 6px rgba(var(--color-shadow-rgb), 0.3);
}

.punbb #pun-main>h1 span,
.punbb #pun-stats h2 span,
.forum h2 .item1 {
  font-family: var(--font-heading);
  font-size: 1.07143rem;
  letter-spacing: 3px;
  color: var(--color-text-on-dark);
  text-transform: uppercase;
}

/* "Ôîðóì" label — redundant under our header on index */
.punbb #pun-index #pun-main>h1 {
  display: none;
}

/* pagination + new-topic/new-poll toolbar merged into the h1 banner row */
.punbb #pun-main>h1+.linkst {
  grid-column: 1;
  grid-row: 1;
  position: relative;
  z-index: 2;
  margin: 0;
  color: var(--color-text-on-dark-muted);
}

.punbb #pun-main>h1+.linkst .pagelink strong {
  color: var(--color-accent-bright);
}

/* drop the parchment-page button chrome — on the banner these read as native
   header controls, pinned flush to the far right edge */
.punbb #pun-main>h1+.linkst .postlink {
  margin-left: auto;
  gap: 0;
}

.punbb #pun-main>h1+.linkst .postlink a {
  background: none;
  border: none;
  border-radius: 0;
  padding: calc(0.25 * var(--base-spacing)) calc(1.25 * var(--base-spacing));
  font-size: 0.75rem;
  letter-spacing: 1px;
  color: var(--color-text-on-dark-muted);
}

.punbb #pun-main>h1+.linkst .postlink span+span {
  border-left: 1px solid rgba(var(--color-accent-rgb), 0.4);
}

.punbb #pun-main>h1+.linkst .postlink a:hover {
  color: var(--color-accent-bright);
}

/* #pun-stats also matches .punbb .section above, which already forces
   border/box-shadow to none (!important) and sets the same font-family,
   color and backdrop-filter — only the background actually differs here */
.punbb #pun-stats {
  background: var(--color-panel-bg-alt);
  width: 100%;
  box-sizing: border-box;
}

.punbb #pun-stats h2 {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: calc(1.25 * var(--base-spacing));
  font-family: var(--font-heading);
  font-size: 0.92857rem;
  letter-spacing: 2px;
  color: var(--color-heading);
  text-transform: uppercase;
  background: linear-gradient(180deg, var(--color-banner-bg-light) 0%, var(--color-banner-bg) 100%);
  border-top: 2px solid var(--color-accent);
  border-bottom: 2px solid var(--color-accent);
  padding: calc(1.5 * var(--base-spacing)) calc(2.5 * var(--base-spacing));
  margin: 0 calc(-2 * var(--base-spacing));
  box-shadow: 0 2px 6px rgba(var(--color-shadow-rgb), 0.3);
}

.punbb #pun-stats ul {
  list-style: none;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: calc(0.5 * var(--base-spacing)) calc(3 * var(--base-spacing));
  font-size: 0.89286rem;
  color: var(--color-text-muted);
  padding: 0;
}

#pun-stats .statscon {
  background: transparent;
  border-top: none;
  box-shadow: inset 0 1px 0 rgba(var(--color-highlight-rgb), 0.3);
  padding: calc(3 * var(--base-spacing)) calc(2.5 * var(--base-spacing));
}

#pun-stats ul.container li {
  padding: calc(0.25 * var(--base-spacing)) calc(0.75 * var(--base-spacing));
  border-radius: 2px;
  transition: background 0.15s;
}

#pun-stats ul.container li:hover {
  background: rgba(var(--color-border-rgb), 0.08);
}

/* these two can carry a full user list/line, so give them their own row in the flex-wrap container instead of sharing one with the short stats */
#pun-stats .statscon .onlinelist,
#pun-stats .statscon .users_24h {
  width: 100%;
}

#pun-stats .statscon .onlinelist {
  margin-top: calc(4 * var(--base-spacing));
}

#pun-stats li,
#pun-stats span {
  font-family: var(--font-body);
  color: var(--color-text);
  font-size: 0.92857rem;
}

.punbb #pun-stats strong {
  font-family: var(--font-body);
  color: var(--color-heading);
  font-size: 0.92857rem;
  font-weight: 600;
}

.punbb #pun-stats a {
  color: var(--color-link);
  text-decoration: none;
}

.punbb #pun-stats a:hover {
  color: var(--color-heading);
}

/* #pun-about also matches .punbb .section above (same background too here,
   nothing to override) — only padding/text-align add anything new */
.punbb #pun-about {
  padding: calc(1.5 * var(--base-spacing)) calc(3 * var(--base-spacing)) calc(2.5 * var(--base-spacing));
  text-align: center;
}

.punbb #pun-about a {
  font-family: var(--font-body);
  color: var(--color-link);
  text-decoration: none;
  font-size: 0.85714rem;
  letter-spacing: 0.5px;
}

.punbb #pun-about a:hover {
  color: var(--color-heading);
}

#pun-about .item1 {
  padding-left: 0;
}

/* partner-forum banners (forms/footer.html); hidden until a banner is pasted in */
.punbb .partner-banners {
  padding: calc(2.5 * var(--base-spacing)) calc(2 * var(--base-spacing));
}

.punbb .partner-banners:not(:has(img)) {
  display: none;
}

.partner-banners-list {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--base-spacing);
  /* three banner rows tall, the rest scrolls */
  max-height: calc(3 * 31px + 2 * var(--base-spacing));
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--color-accent) transparent;
}

/* every partner banner is a standard 88x31 button; fixed size so the row
   doesn't reflow while the images load */
.partner-banners-list img {
  display: block;
  width: 88px;
  height: 31px;
  opacity: 0.8;
  transition: opacity 0.15s;
}

.partner-banners-list a:hover img {
  opacity: 1;
}

/* ============ FORUM BODY (category + listing) ============ */
/* .forum (topic listing inside a forum, on viewforum pages) reuses this same
   row/table layout — its content differs (topics not forums, no h3 wrapper, plain
   byuser instead of lastpost), so only the shared structural rules below are
   extended to it; markup-specific rules follow in their own TOPIC LISTING block */
.category,
.forum {
  font-family: var(--font-body);
  margin: 0;
}

.category h2 {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: calc(1.25 * var(--base-spacing));
  background: linear-gradient(180deg, var(--color-banner-bg-light) 0%, var(--color-banner-bg) 100%);
  border-top: 2px solid var(--color-accent);
  border-bottom: 2px solid var(--color-accent);
  padding: calc(1.5 * var(--base-spacing)) calc(2.5 * var(--base-spacing));
  margin: 0;
  text-align: center;
  box-shadow: 0 2px 6px rgba(var(--color-shadow-rgb), 0.3);
}

.category h2::before {
  content: "";
  position: absolute;
  top: 50%;
  left: calc(3 * var(--base-spacing));
  right: calc(3 * var(--base-spacing));
  height: 1px;
  background: rgba(var(--color-accent-light-rgb), 0.5);
}

.category h2::after {
  right: calc(3 * var(--base-spacing));
}

.category h2 span {
  font-family: var(--font-heading);
  font-size: 1.07143rem;
  letter-spacing: 3px;
  background: linear-gradient(180deg, var(--color-banner-bg-light) 0%, var(--color-banner-bg) 100%);
  color: var(--color-text-on-dark);
  text-transform: uppercase;
  position: relative;
  z-index: 2;
  padding: 0 calc(3 * var(--base-spacing));
}

.category .catleft,
.category .catright {
  display: none;
}

.category .container,
.forum .container {
  border-top: none;
  padding: 0;
  box-shadow: inset 0 1px 0 rgba(var(--color-highlight-rgb), 0.3);
}

.category table,
.forum table {
  width: 100%;
  border-collapse: collapse;
}

.category thead,
.forum thead {
  display: none;
}

.category tbody tr {
  position: relative;
  display: flex;
  flex-wrap: nowrap;
  align-items: flex-start;
  padding: calc(1.625 * var(--base-spacing)) calc(2.5 * var(--base-spacing));
  border-bottom: 1px solid rgba(var(--color-border-rgb), 0.3);
  border-left: 5px solid transparent;
  /* default = read state */
  transition: background 0.15s;
}

/* no description/tags line here (unlike .category), so a plain wrapping flex
   row lays tcl/tc2/tc3/tcr out directly — no need for .category's
   force-wrap-then-absolutely-position-the-second-line trick */
.forum tbody tr {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  padding: calc(1.625 * var(--base-spacing)) calc(2.5 * var(--base-spacing));
  border-bottom: 1px solid rgba(var(--color-border-rgb), 0.3);
  border-left: 5px solid transparent;
  transition: background 0.15s;
}

.category tbody tr:last-child,
.forum tbody tr:last-child {
  border-bottom: none;
}

.category tbody tr:hover,
.forum tbody tr:hover {
  background: rgba(var(--color-border-rgb), 0.08);
}

/* unread state — sole indicator is this border + tint, no dedicated dot/icon.
   .inew is the real forum class (confirmed on #messages markup) */
.category tbody tr.inew,
.forum tbody tr.inew,
#messages tbody tr.inew {
  border-left: 5px solid var(--color-accent);
  background: rgba(var(--color-accent-rgb), 0.1);
  box-shadow: inset calc(0.5 * var(--base-spacing)) 0 calc(1 * var(--base-spacing)) calc(-0.5 * var(--base-spacing)) rgba(var(--color-accent-rgb), 0.5);
}

/* ---- LINE 1: icon + title + description + tags — full width, forces the wrap ---- */
.category td.tcl {
  display: flex;
  align-items: flex-start;
  gap: calc(1.25 * var(--base-spacing));
  flex: 0 1 auto;
  width: 100%;
  padding: 0;
}

.forum td.tcl {
  display: flex;
  align-items: center;
  gap: calc(1.25 * var(--base-spacing));
  flex: 1 1 auto;
  min-width: 0;
  padding: 0;
}

.category .intd,
.forum .intd,
#messages .intd {
  display: flex;
  align-items: flex-start;
  gap: calc(1.25 * var(--base-spacing));
  width: 100%;
}

/* forum lists and #messages have no status icon — the row's left border (see tr.inew above) is the only indicator */
.category .icon,
#messages .icon {
  display: none;
}

/* topic status glyph replaces the "Âàæíî:" / "Çàêðûòà" text labels; .icon
   itself is empty markup, so the glyph is its ::before. Plain and .inew
   (new replies) topics share one bubble and differ only by color; sticky/closed
   swap the glyph, and .inew still recolors them */
.forum .icon {
  flex: 0 0 auto;
  width: calc(2 * var(--base-spacing));
  text-align: center;
  line-height: 1.4;
  color: var(--color-text-muted);
}

.forum .icon::before {
  content: "\f075";
  font: normal 900 0.85714rem "Font Awesome 6 Free";
}

.forum tr.isticky .icon {
  color: var(--color-accent);
}

.forum tr.inew .icon {
  color: var(--color-accent-bright);
}

.forum tr.isticky .icon::before {
  content: "\f08d";
  font-weight: 900;
}

.forum tr.iclosed .icon::before {
  content: "\f023";
  font-weight: 900;
}

.forum .stickytext,
.forum .closedatafield {
  display: none;
}

.category .tclcon,
.forum .tclcon,
#messages .tclcon {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 0.96429rem;
  color: var(--color-text-muted);
  line-height: 1.4;
  text-align: justify;
}

.category .tclcon h3 {
  margin: 0 0 calc(0.375 * var(--base-spacing));
  font-size: 1.14286rem;
  min-width: 0;
}

/* ellipsis has to live on the link, not the h3 — the h3 is a flex item now
   (see .forum-meta-row h3), and flex containers don't truncate their own text */
.category .tclcon h3 a {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-heading);
  color: var(--color-heading);
  text-decoration: none;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.category .tclcon h3 a:hover {
  color: var(--color-heading-hover);
}

/* description links rendered as compact pill tags — the last-post avatar link
   also lives inside .tclcon (forum-meta-row is nested there), so it's excluded
   here rather than picking up pill margin/border/background too */
.category .forum-links {
  margin-top: calc(2 * var(--base-spacing));
}

.category .forum-links a {
  display: inline-block;
  font-style: normal;
  text-transform: uppercase;
  font-size: 0.71429rem;
  letter-spacing: 0.5px;
  color: var(--color-text-muted);
  text-decoration: none;
  background: rgba(var(--color-border-rgb), 0.12);
  border: 1px solid rgba(var(--color-border-rgb), 0.3);
  border-radius: 10px;
  padding: calc(0.25 * var(--base-spacing)) calc(1.125 * var(--base-spacing));
  margin: calc(0.75 * var(--base-spacing)) calc(0.5 * var(--base-spacing)) 0 0;
}

.category .forum-links a:hover {
  background: rgba(var(--color-accent-rgb), 0.25);
  color: var(--color-heading);
}

/* ---- LINE 2: counts + last-post — footer JS wraps tc2/tc3/tcr together with
   h3 into .forum-meta-row, so they flex-position off the title itself instead
   of relying on fixed pixel offsets ---- */
.category .forum-meta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(0.5 * var(--base-spacing)) calc(1.75 * var(--base-spacing));
  margin: calc(0.375 * var(--base-spacing)) 0 calc(0.75 * var(--base-spacing));
}

/* title now lives inside the same flex row as the counts (JS moved h3 in here) —
   let it fill whatever width tc2/tc3/tcr don't take, truncating instead of wrapping */
.category .forum-meta-row h3 {
  display: flex;
  align-items: center;
  flex: 1 1 auto;
  margin: 0 0 calc(0.375 * var(--base-spacing));
}

.category .forum-meta-row td.tc2,
.category .forum-meta-row td.tc3 {
  display: flex;
  align-items: center;
  gap: calc(0.5 * var(--base-spacing));
  width: auto;
  padding-right: calc(1.75 * var(--base-spacing));
  border-right: 1px solid rgba(var(--color-border-rgb), 0.3);
  font-size: 0.82143rem;
  color: var(--color-text);
  white-space: nowrap;
}

.category .forum-meta-row td.tc2::after {
  content: " òåì";
  color: var(--color-link);
  font-size: 0.75rem;
}

.category .forum-meta-row td.tc3::after {
  content: " ñîîáù.";
  color: var(--color-link);
  font-size: 0.75rem;
}

/* avatar | link+timestamp layout — br between lastpost-link and .lastpost is
   forum-generated markup we can't touch, so it's hidden and grid auto-placement
   (row-major, sparse) drops the link into col2/row1 and .lastpost into col2/row2
   on its own once the avatar claims col1 across both rows */
.category .forum-meta-row td.tcr,
.forum td.tcr {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: calc(1 * var(--base-spacing));
  font-size: 0.82143rem;
  color: var(--color-text-muted);
  text-align: left;
}

.category .forum-meta-row td.tcr {
  width: 220px;
  min-width: 220px;
}

.category .forum-meta-row td.tcr br,
.forum td.tcr br {
  display: none;
}

.category .forum-meta-row td.tcr .user-avatar,
.forum td.tcr .user-avatar {
  grid-row: 1 / 3;
  grid-column: 1;
  align-self: stretch;
}

/* .forum (topic listing) — plain in-flow flex columns, no position:absolute
   trick: the single-line title + byuser here doesn't need the two-line
   force-wrap that .category's description/tags block requires */
.forum td.tc2,
.forum td.tc3 {
  flex: 0 0 auto;
  width: 72px;
  padding: 0 calc(1 * var(--base-spacing));
  border-right: 1px solid rgba(var(--color-border-rgb), 0.3);
  text-align: center;
  font-size: 0.82143rem;
  color: var(--color-text);
  white-space: nowrap;
}

.forum td.tcr {
  flex: 0 0 auto;
  width: 220px;
  padding-left: calc(1 * var(--base-spacing));
}

.category .user-avatar,
.forum .user-avatar,
#messages .user-avatar {
  position: relative;
  flex: 0 0 auto;
}

.punbb .user-avatar {
  width: auto !important;
  height: auto !important;
  margin: 0 !important;
  float: none !important;
}

/* avatars are circles, so the dot sits at the corner of the box rather than
   past it — any further out and it floats free of the round edge */
.punbb .user-avatar .isonline {
  top: 0 !important;
  right: 0 !important;
}

/* extra.css (uncontrolled, loaded on the live forum) targets this exact
   selector — .punbb .user-avatar .avatar-image — at 3-class specificity, so
   !important is needed throughout to keep our plain circle instead of its
   36px/opacity/float version */
.category .avatar-image,
.forum .avatar-image,
.usertable .avatar-image,
#messages .avatar-image {
  display: block !important;
  width: 26px !important;
  height: 26px !important;
  border-radius: 50% !important;
  background-size: cover !important;
  background-position: center center !important;
  background-repeat: no-repeat !important;
  border: none !important;
  overflow: hidden !important;
  opacity: 1 !important;
  float: none !important;
}

/* last-post avatar sized to match the two stacked text rows beside it —
   height:100%/aspect-ratio on an auto-sized grid track is circular (column
   width depends on avatar width, which depends on a row height not known
   until column widths are settled) and collapses the avatar to 0&#215;0, so this
   uses a fixed square instead */
.category .forum-meta-row td.tcr .avatar-image,
.forum td.tcr .avatar-image {
  width: 35px !important;
  height: 35px !important;
}

.category .isonline,
.forum .isonline,
.usertable .isonline,
#messages .isonline {
  position: absolute;
  bottom: calc(-0.125 * var(--base-spacing));
  right: calc(-0.125 * var(--base-spacing));
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--color-status-online);
  border: 1px solid var(--color-panel-bg);
}

.category .lastpost-link {
  color: var(--color-heading);
  text-decoration: none;
  font-style: italic;
  margin: 0 !important;
}

.category .lastpost {
  display: block;
  font-size: 0.71429rem;
  color: var(--color-link);
  margin-top: calc(0.125 * var(--base-spacing));
}

/* ============ TOPIC LISTING (.forum markup-specific) ============ */
/* .forum h2 (topic-count/pagination banner) is unconditionally hidden by the
   `display: none !important` combinator at the bottom of this file, so it's
   left unstyled here rather than styling an element that never renders */

.forum .tclcon a,
#messages .tclcon a {
  font-family: var(--font-heading);
  font-size: 1.07143rem;
  color: var(--color-heading);
  text-decoration: none;
  letter-spacing: 0.5px;
}

.forum .tclcon a:hover,
#messages .tclcon a:hover {
  color: var(--color-heading-hover);
}

.forum .tclcon .byuser {
  font-size: 0.78571rem;
  font-style: italic;
  color: var(--color-link);
}

.forum td.tcr a {
  color: var(--color-heading);
  text-decoration: none;
  font-style: italic;
}

.forum td.tcr .byuser {
  display: block;
  margin-top: calc(0.125 * var(--base-spacing));
  font-size: 0.71429rem;
  color: var(--color-link);
}

/* search/active-topics reuses the exact .forum row markup, but tc2 there
   holds the source forum's name (a link, arbitrary length) instead of a
   short reply count — the 72px/nowrap sizing meant for numbers lets it
   overflow past its border straight into tc3's count */
#pun-searchtopics .forum td.tc2 {
  width: calc(15 * var(--base-spacing));
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: left;
}

/* ============ PLAIN DATA TABLES (online.php list + userlist.php + respect.php) ============ */
/* .online-list, .usertable and #pun-respect's table are real tabular grids
   with a visible header — unlike .category/.forum's card-style rows above,
   so they share one flat table skin instead of duplicating it per page.
   .online-list and #pun-respect's wrapper both carry the generic .section
   class, which forces a near-opaque dark background (see .punbb .section)
   meant for chrome like crumbs/status bars; overridden here to the same
   light parchment sub-surface .usertable already sits on (it isn't
   .section at all, so it never picked up the dark override), so all three
   read as one content panel like the rest of the page. #pun-respect's
   wrapper has no distinguishing class of its own (just "section"), so it's
   scoped by id instead — specifically #pun-main's child, since #pun-respect
   also wraps page chrome (logo/navlinks/crumbs/ad slots) that all carry
   the same bare .section class and must stay on the dark chrome background */
.punbb .section.online-list,
#pun-respect #pun-main .section {
  background: var(--color-panel-bg-alt);
}

.online-list .container,
.usertable .container,
#pun-respect .container {
  padding: calc(1.5 * var(--base-spacing)) calc(2.5 * var(--base-spacing));
}

.online-list table,
.usertable table,
#pun-respect table,
#filetable {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-body);
}

.online-list thead th,
.usertable thead th,
#pun-respect thead th,
#filetable thead th {
  text-align: left;
  font-family: var(--font-heading);
  font-size: 0.78571rem;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--color-link);
  border-bottom: 2px solid var(--color-accent);
  padding: calc(0.75 * var(--base-spacing)) calc(1 * var(--base-spacing)) calc(0.75 * var(--base-spacing)) 0;
}

.online-list thead th.time,
.usertable thead th.tc3,
#filetable thead th.tc3 {
  text-align: right;
}

/* #pun-respect's own tc3 is "Êòî" (a name), not a time column like
   .usertable's — only its rightmost column (tcr, "Â òåìå") gets the
   right-align treatment, on both header and cells */
#pun-respect thead th.tcr,
#pun-respect td.tcr {
  text-align: right;
}

/* the live forum's own uncontrolled global stylesheet pins bare
   `.tcl`/`.tc2`/`.tc3`/`.tcr` to fixed width percentages (20/10/20/50%) and
   `td.tc2` to a larger font-size, `!important` — leftover badge styling for
   its classic topic-list replies/views columns. #pun-respect reuses those
   same class names for unrelated columns, so the squeeze bleeds in: "Çà
   ÷òî" (tc3.tc4) gets crushed into a 20%-wide column and wraps into a tall,
   mostly-blank ribbon instead of using the row's/card's full width, on both
   the desktop table and the stacked mobile card. `#pun-respect`'s id lifts
   specificity above the bare classes, so matching their `!important` here
   is enough to win outright — `width: auto` then means "size the desktop
   column by content" and "fill the mobile card's full width", each in its
   own display context. */
#pun-respect th.tcl,
#pun-respect th.tc2,
#pun-respect th.tc3,
#pun-respect th.tcr,
#pun-respect td.tcl,
#pun-respect td.tc2,
#pun-respect td.tc3,
#pun-respect td.tcr {
  width: auto !important;
}

#pun-respect td.tc2 {
  font-size: 0.85714rem !important;
}

.online-list tbody tr,
.usertable tbody tr,
#pun-respect tbody tr,
#filetable tbody tr {
  border-bottom: 1px solid rgba(var(--color-border-rgb), 0.3);
  transition: background 0.15s;
}

.online-list tbody tr:last-child,
.usertable tbody tr:last-child,
#pun-respect tbody tr:last-child,
#filetable tbody tr:last-child {
  border-bottom: none;
}

.online-list tbody tr:hover,
.usertable tbody tr:hover,
#pun-respect tbody tr:hover,
#filetable tbody tr:hover {
  background: rgba(var(--color-border-rgb), 0.08);
}

.online-list td,
.usertable td,
#pun-respect td,
#filetable td {
  padding: calc(1 * var(--base-spacing)) calc(1 * var(--base-spacing)) calc(1 * var(--base-spacing)) 0;
  font-size: 0.85714rem;
  color: var(--color-text-muted);
  vertical-align: top;
}

.online-list td.tc3,
.usertable td.tc3,
#filetable td.tc3 {
  text-align: right;
  white-space: nowrap;
}

.online-list td:last-child,
.usertable td:last-child,
.online-list th:last-child,
.usertable th:last-child,
#pun-respect td:last-child,
#pun-respect th:last-child,
#filetable td:last-child,
#filetable th:last-child {
  padding-right: 0;
}

.online-list td a,
.usertable td a,
#pun-respect td a,
#filetable td a {
  color: var(--color-link);
  text-decoration: none;
}

.online-list td a:hover,
.usertable td a:hover,
#pun-respect td a:hover,
#filetable td a:hover {
  color: var(--color-heading-hover);
  text-decoration: underline;
}

/* the whole cell gets the heading treatment (not just an `a.registered-user`
   selector) so a guest row's bare "Ãîñòü" text — no link at all — reads with
   the same weight as a member's name; an admin viewing this table also gets
   a second link per row (the visitor's IP, for moderate.php) inside the same
   cell, reset back to a plain link separately below so it doesn't inherit it */
.online-list td.username,
.usertable td.username .usersname a {
  font-family: var(--font-heading);
  font-weight: 600;
  letter-spacing: 0.5px;
  color: var(--color-heading);
}

.online-list td.username a:first-child:hover,
.usertable td.username .usersname a:hover {
  color: var(--color-heading-hover);
}

/* filename is #filetable's identity column, same weight as usertable's username */
#filetable td.tcl a {
  font-family: var(--font-heading);
  font-weight: 600;
  letter-spacing: 0.3px;
  color: var(--color-heading);
}

#filetable td.tcl a:hover {
  color: var(--color-heading-hover);
}

#filetable input[type="checkbox"] {
  accent-color: var(--color-accent);
  cursor: pointer;
}

.online-list td.username a:not(:first-child) {
  font-family: var(--font-body);
  font-weight: normal;
  letter-spacing: normal;
  color: var(--color-link);
}

/* ============ PAGE TOOLBAR (pagelink / postlink / modmenu) ============ */
/* shared by the topic listing (.linkst/.linksb around .forum) and the topic
   view (.linkst/.linksb around #topic_t1) — same markup on both pages */
.punbb .linkst {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: calc(1 * var(--base-spacing)) calc(2 * var(--base-spacing));
  padding: calc(1.5 * var(--base-spacing)) calc(2.5 * var(--base-spacing));
  font-size: 0.85714rem;
  color: var(--color-text-muted);
}

.punbb .linksb {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: calc(1 * var(--base-spacing)) calc(2 * var(--base-spacing));
  padding: calc(1.5 * var(--base-spacing)) calc(2.5 * var(--base-spacing));
  font-size: 0.85714rem;
  color: var(--color-text-muted);
  border-top: 1px dashed rgba(var(--color-border-rgb), 0.3);
}

.punbb .pagelink strong {
  color: var(--color-heading);
}

/* .linksb markup order differs from .linkst (postlink, pagelink, subscribelink) —
   reorder visually to match .linkst's pagelink-then-postlink order. On the forum
   listing page .postlink is wrapped in a <noindex> tag, which is the actual flex
   item there — order has to target that wrapper too or it's ignored */
.punbb .linksb .pagelink {
  order: 1;
  flex-grow: 1;
}

.punbb .linksb .postlink,
.punbb .linksb noindex {
  order: 2;
}

.punbb .linksb .subscribelink {
  order: 3;
}

.punbb .postlink {
  display: flex;
  gap: calc(1 * var(--base-spacing));
}

.punbb .postlink a,
.punbb .subscribelink a {
  display: inline-block;
  font-family: var(--font-heading);
  font-size: 0.78571rem;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--color-text-on-dark);
  text-decoration: none;
  background: linear-gradient(180deg, var(--color-banner-bg-light) 0%, var(--color-banner-bg) 100%);
  border: 1px solid var(--color-accent);
  border-radius: calc(0.5 * var(--base-spacing));
  padding: calc(0.5 * var(--base-spacing)) calc(1.25 * var(--base-spacing));
}

.punbb .postlink a:hover,
.punbb .subscribelink a:hover {
  border-color: var(--color-accent-hover);
  color: var(--color-accent-bright);
}

.punbb .modmenu {
  padding: calc(1.25 * var(--base-spacing)) calc(2.5 * var(--base-spacing));
  font-size: 0.78571rem;
  color: var(--color-text-muted);
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: calc(0.5 * var(--base-spacing)) calc(2 * var(--base-spacing));
  border-top: 1px dashed rgba(var(--color-border-rgb), 0.3);
}

.punbb .modmenu a {
  color: var(--color-link);
}

.punbb .modmenu a:hover {
  color: var(--color-heading);
}

.punbb .modmenu select {
  color: var(--color-link);
  font-family: var(--font-body);
  font-size: 0.78571rem;
  background: var(--color-panel-bg);
  border: 1px solid var(--color-border);
  border-radius: calc(0.375 * var(--base-spacing));
  padding: calc(0.25 * var(--base-spacing)) calc(0.5 * var(--base-spacing));
}

/* top fade removed — the dark #pun-navlinks bar right after the header
   handles the dark-to-cream transition directly, no overlay needed */
#html-header+#pun-break1 {
  padding: 0 !important;
}

/* dark tone continues from the header's greeting bar, through the divider and
   breadcrumb, down to the category header — cream only starts below it */
.punbb #pun-break1,
.punbb #pun-break2 {
  background: var(--color-banner-bg) !important;
}

.punbb #pun-break1 hr,
.punbb #pun-break2 hr {
  border: none;
  border-top: 1px solid rgba(var(--color-accent-light-rgb), 0.3);
  margin: 0 calc(2.5 * var(--base-spacing));
}

/* ============ REUSABLE BUTTON ============ */
.button {
  display: inline-block;
  font-family: var(--font-heading);
  font-size: 0.85714rem;
  letter-spacing: 1px;
  font-weight: 500;
  text-transform: uppercase;
  color: var(--color-text-on-dark);
  background: linear-gradient(180deg, var(--color-banner-bg-light) 0%, var(--color-banner-bg) 100%);
  border: 1px solid var(--color-accent);
  border-radius: calc(0.5 * var(--base-spacing));
  padding: calc(0.75 * var(--base-spacing)) calc(2 * var(--base-spacing));
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(var(--color-shadow-rgb), 0.3);
  transition: background 0.25s ease, color 0.25s ease, box-shadow 0.25s ease, transform 0.15s ease;
}

.button:hover {
  border-color: var(--color-accent-hover);
  box-shadow: 0 4px 10px rgba(var(--color-shadow-rgb), 0.35), 0 0 0 1px rgba(var(--color-accent-rgb), 0.2);
  transform: translateY(-1px);
}

.button:active {
  transform: translateY(0);
  box-shadow: 0 1px 3px rgba(var(--color-shadow-rgb), 0.3);
}

/* ============ REPUTATION MODAL ("Ïîäíÿòü ðåïóòàöèþ") ============ */
/* #pun-reputation is injected unstyled by the forumscripts.ru post_voters
   plugin (third-party, not in forms/) — theme it to match the rest of the site */
#pun-reputation .bg.post_reputation {
  background: rgba(var(--color-bg-rgb), 0.75);
}

#pun-reputation .inner.post_reputation {
  background: var(--color-panel-bg);
  border: 1px solid var(--color-border);
  border-top: 2px solid var(--color-accent);
  border-radius: calc(0.5 * var(--base-spacing));
  box-shadow: 0 10px 24px rgba(var(--color-shadow-rgb), 0.5);
  /* the plugin centers this by writing an inline top/left in px, sized for its
     own (unstyled) natural height — that math doesn't know about max-height
     below, so on a short viewport the box still starts too low and runs off
     the bottom. Re-center with a fixed anchor point instead (!important to
     beat the inline style) so it always fits, and scroll instead of clipping
     when content is still taller than that */
  position: fixed !important;
  top: 50% !important;
  left: 50% !important;
  margin: 0 !important;
  transform: translate(-50%, -50%);
  max-height: 90vh;
  overflow-y: auto;
}

#pun-reputation h1 {
  margin: 0;
  padding: calc(1.25 * var(--base-spacing)) calc(2 * var(--base-spacing));
  background: linear-gradient(180deg, var(--color-banner-bg-light) 0%, var(--color-banner-bg) 100%);
  border-bottom: 1px solid var(--color-accent);
  /* .inner's overflow is now auto (scrollable), not hidden, so this needs its
     own top radius to keep the rounded corners instead of relying on clipping */
  border-radius: calc(0.5 * var(--base-spacing) - 1px) calc(0.5 * var(--base-spacing) - 1px) 0 0;
}

#pun-reputation h1 span {
  font-family: var(--font-heading);
  font-size: 1.14286rem;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--color-text-on-dark);
}

#pun-reputation .container {
  padding: calc(2 * var(--base-spacing)) !important;
  font-family: var(--font-body);
  color: var(--color-text);
}

#pun-reputation table {
  width: 100%;
  border-collapse: collapse;
}

#pun-reputation td {
  padding: calc(0.75 * var(--base-spacing)) 0;
  vertical-align: top;
}

#pun-reputation td.tcl {
  padding-right: calc(1.5 * var(--base-spacing));
  font-family: var(--font-heading);
  font-weight: 600;
  letter-spacing: 0.5px;
  color: var(--color-heading);
  white-space: nowrap;
}

#pun-reputation #reputationText {
  width: 100% !important;
  box-sizing: border-box;
}

#pun-reputation .button {
  margin-right: calc(0.5 * var(--base-spacing));
}

.postlink.long {
  width: auto !important;
}

.post-sig dt {
  display: none;
}

.post-sig dd {
  margin: 0;
  padding: 0;
}

/* ============ PROFILE MASK MODAL (#mask_dialog) ============ */
/* injected unstyled by the post-mask plugin (third-party, not in forms/). Its
   own layout (floated columns, inline px sizes) is overridden with a grid:
   preview | form | saved masks, stacked on narrow screens. */
.hv-bg {
  position: fixed !important;
  inset: 0 !important;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(2 * var(--base-spacing));
  /* plugin paints a purple image here */
  background: rgba(var(--color-bg-rgb), 0.8) !important;
  background-image: none !important;
}

/* plugin floats/sizes these and hides the preview on narrow screens */
.hv-preview-block,
.hv-form-block,
.hv-masks-storage {
  position: static !important;
  float: none !important;
  width: auto !important;
  height: auto !important;
  margin: 0 !important;
}

/* preview and saved masks read as two inset cards, the form as the open area */
.hv-preview-block,
.hv-masks-storage {
  padding: calc(1.25 * var(--base-spacing)) !important;
  background: var(--color-panel-bg-alt);
  border: 1px solid rgba(var(--color-border-rgb), 0.5);
  border-radius: calc(0.5 * var(--base-spacing));
}

.hv-preview-block {
  display: block !important;
}

.hv-bg > .inner {
  position: relative !important;
  inset: auto !important;
  margin: 0 !important;
  width: 100% !important;
  max-width: 1080px !important;
  height: auto !important;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--color-panel-bg) !important;
  background-image: none !important;
  border: 1px solid var(--color-border);
  border-top: 2px solid var(--color-accent);
  border-radius: calc(0.5 * var(--base-spacing));
  box-shadow: 0 10px 24px rgba(var(--color-shadow-rgb), 0.5);
  font-family: var(--font-body);
  color: var(--color-text);
  overflow: hidden;
}

.hv-mask-dialog-title {
  flex-shrink: 0;
  padding: calc(1.25 * var(--base-spacing)) calc(2 * var(--base-spacing));
  background: var(--gradient-banner);
  border-bottom: 1px solid var(--color-accent);
  font-family: var(--font-heading);
  font-size: 1.14286rem;
  letter-spacing: 1px;
  text-transform: uppercase;
  text-align: center;
  color: var(--color-text-on-dark);
}

.hv-error-list {
  margin: 0;
  padding: calc(1 * var(--base-spacing)) calc(2 * var(--base-spacing));
  list-style: none;
  color: var(--color-heading-hover);
}

.hv-mask-block {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: grid !important;
  grid-template-columns: calc(18 * var(--base-spacing)) minmax(0, 1fr) calc(22 * var(--base-spacing));
  gap: calc(2 * var(--base-spacing));
  align-items: start;
  padding: calc(2 * var(--base-spacing));
}

.hv-preview-block {
  text-align: center;
}

.hv-preview-author {
  font-family: var(--font-heading);
  font-weight: 600;
  color: var(--color-heading);
  overflow-wrap: anywhere;
}

.hv-preview-title {
  margin-bottom: calc(1.25 * var(--base-spacing));
  padding-bottom: calc(1 * var(--base-spacing));
  border-bottom: 1px solid rgba(var(--color-border-rgb), 0.35);
  font-size: 0.85714rem;
  color: var(--color-text-muted);
  overflow-wrap: anywhere;
}

.hv-preview-avatar {
  width: calc(14 * var(--base-spacing));
  max-width: 100%;
  margin: 0 auto;
}

.hv-preview-avatar img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--color-accent);
  border-radius: calc(0.5 * var(--base-spacing));
}

.hv-preview-pafld1 {
  margin-top: calc(1 * var(--base-spacing));
  font-size: 0.85714rem;
  overflow-wrap: anywhere;
}

/* the plugin gives the form its own scroll box, which pushes a scrollbar over the inputs */
.hv-form-block {
  min-width: 0;
  overflow: visible !important;
}

.hv-mask-field {
  padding-bottom: calc(1.5 * var(--base-spacing));
  margin-bottom: calc(1.5 * var(--base-spacing));
  border-bottom: 1px dashed rgba(var(--color-border-rgb), 0.4);
}

.hv-mask-field:last-child {
  padding-bottom: 0;
  margin-bottom: 0;
  border-bottom: 0;
}

.hv-mask-field label {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 calc(1 * var(--base-spacing));
  margin-bottom: calc(0.5 * var(--base-spacing));
}

.hv-mask-field label b {
  font-family: var(--font-heading);
  font-weight: 600;
  letter-spacing: 0.5px;
  color: var(--color-heading);
}

.hv-mask-field .description {
  flex-basis: 100%;
  font-size: 0.78571rem;
  color: var(--color-text-muted);
}

.hv-mask-field input,
.hv-mask-field textarea {
  width: 100%;
}

.hv-mask-field textarea {
  min-height: calc(12 * var(--base-spacing));
  resize: vertical;
}

/* "« âñòàâèòü øàáëîí" sits before the label text in the markup — push it right */
.hv-add-template {
  order: 1;
  margin-left: auto;
  padding: calc(0.25 * var(--base-spacing)) calc(1 * var(--base-spacing));
  font-size: 0.71429rem;
}

.hv-masks-storage {
  display: grid !important;
  grid-template-columns: repeat(auto-fill, minmax(calc(8 * var(--base-spacing)), 1fr)) !important;
  align-content: start;
  gap: calc(1 * var(--base-spacing));
  max-height: calc(48 * var(--base-spacing));
  overflow-y: auto;
  list-style: none;
  min-width: 0;
}

.hv-storage-count {
  grid-column: 1 / -1 !important;
  float: none !important;
  width: auto !important;
  margin-left: 0 !important;
  margin-right: 0 !important;
  margin-bottom: calc(0.25 * var(--base-spacing));
  padding-bottom: calc(1 * var(--base-spacing));
  border-bottom: 1px solid rgba(var(--color-border-rgb), 0.35);
  font-size: 0.78571rem;
  text-align: center;
  color: var(--color-text-muted);
}

/* each saved mask is a tile: thumbnail over a pill-shaped delete action. The
   plugin sets fixed widths/floats on the li, hence the resets */
.hv-mask-element {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(0.75 * var(--base-spacing));
  min-width: 0;
  float: none !important;
  width: auto !important;
  height: auto !important;
  margin: 0 !important;
  padding: calc(0.5 * var(--base-spacing)) !important;
  background: var(--color-panel-bg);
  border: 1px solid rgba(var(--color-border-rgb), 0.5);
  border-radius: calc(0.5 * var(--base-spacing));
  box-shadow: 0 1px 3px rgba(var(--color-shadow-rgb), 0.15);
}

.hv-mask-element img {
  display: block;
  width: 100% !important;
  height: auto !important;
  border: 1px solid var(--color-border);
  border-radius: calc(0.5 * var(--base-spacing));
}

.hv-delete-mask {
  max-width: 100%;
  padding: calc(0.25 * var(--base-spacing)) calc(0.75 * var(--base-spacing));
  font-family: var(--font-heading);
  font-size: 0.71429rem;
  letter-spacing: 0.25px;
  text-transform: uppercase;
  color: var(--color-heading);
  border: 1px solid var(--color-heading);
  border-radius: calc(0.5 * var(--base-spacing));
  cursor: pointer;
  transition: background 0.25s ease, color 0.25s ease;
}

.hv-delete-mask:hover {
  background: var(--color-heading);
  color: var(--color-text-on-dark);
}

.hv-control {
  flex-shrink: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: calc(1 * var(--base-spacing));
  padding: calc(1.5 * var(--base-spacing)) calc(2 * var(--base-spacing));
  background: var(--color-panel-bg-alt);
  border-top: 1px solid var(--color-border);
}

.hv-topic-mask-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.hv-topic-mask-icon {
  fill: currentColor;
}

.hv-topic-mask-toggle.is-inactive .hv-topic-mask-icon-locked,
.hv-topic-mask-toggle:not(.is-inactive) .hv-topic-mask-icon-unlocked {
  display: none;
}

.hv-topic-mask-toggle:not(.is-inactive) {
  color: var(--color-accent-bright);
  border-color: var(--color-accent-bright);
}

@media (max-width: 640px) {
  .hv-bg {
    padding: calc(1 * var(--base-spacing));
  }

  /* preview and saved masks share a fixed-height first row 50/50, form takes the second */
  .hv-mask-block {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    grid-template-areas:
      "preview storage"
      "form form" !important;
    grid-template-rows: calc(27 * var(--base-spacing)) auto !important;
    align-items: stretch;
    gap: calc(1.5 * var(--base-spacing));
    padding: calc(1.5 * var(--base-spacing));
  }

  /* the id outranks any plugin rule that hides these on narrow screens */
  #mask_dialog .hv-preview-block,
  #mask_dialog .hv-masks-storage {
    visibility: visible !important;
    min-height: 0;
    overflow-y: auto;
  }

  .hv-preview-block {
    grid-area: preview !important;
    display: flex !important;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 100% !important;
    max-width: none !important;
    text-align: center;
  }

  .hv-preview-author,
  .hv-preview-title {
    width: 100%;
  }

  .hv-form-block {
    grid-area: form !important;
    padding-top: calc(1.5 * var(--base-spacing)) !important;
    border-top: 1px solid var(--color-border);
  }

  /* the fixed first row keeps both halves the same height; the masks scroll inside it */
  .hv-masks-storage {
    grid-area: storage !important;
    display: grid !important;
    max-height: none;
  }

  .hv-control {
    padding: calc(1.5 * var(--base-spacing));
  }

  .hv-control .button {
    flex: 1 1 calc(12 * var(--base-spacing));
  }

  .hv-control .hv-topic-mask-toggle {
    flex: 0 0 auto;
  }

  /* 16px+ stops iOS Safari zooming the page on focus */
  .hv-mask-block .hv-mask-field :is(input, textarea) {
    font-size: 1.23077rem;
  }
}

/* ============ REUSABLE FORM FIELDS (generic inputs/selects) ============ */
/* excludes .modmenu's own select, which already carries a complete compact style of
   its own — kept as the sole rule touching that element rather than layering a second */
input[type="text"],
input[type="password"],
input[type="email"],
input[type="search"],
input[type="number"],
input[type="url"],
.hv-mask-field textarea {
  font-family: var(--font-body);
  font-size: 0.92857rem;
  color: var(--color-text);
  background: var(--color-panel-bg);
  border: 1px solid var(--color-border);
  border-radius: calc(0.5 * var(--base-spacing));
  padding: calc(0.75 * var(--base-spacing)) calc(1.25 * var(--base-spacing));
  box-sizing: border-box;
}

select:not(.modmenu *) {
  font-family: var(--font-body);
  font-size: 0.92857rem;
  color: var(--color-text);
  background: var(--color-panel-bg);
  border: 1px solid var(--color-border);
  border-radius: calc(0.5 * var(--base-spacing));
  padding: calc(0.625 * var(--base-spacing)) calc(1.25 * var(--base-spacing));
  box-sizing: border-box;
  cursor: pointer;
}

input[type="text"]:focus,
input[type="password"]:focus,
input[type="email"]:focus,
input[type="search"]:focus,
input[type="number"]:focus,
input[type="url"]:focus,
.hv-mask-field textarea:focus,
select:not(.modmenu *):focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 2px rgba(var(--color-accent-rgb), 0.25);
}

/* ============ POST SYMBOL COUNT (topic post form only, see footer.html) ============ */
.symbol-count {
  margin-top: calc(0.5 * var(--base-spacing));
  font-size: 0.78571rem;
  color: var(--color-text-muted);
  text-align: right;
}

/* ============ FLOAT BUTTONS ([float=left]/[float=right], see footer.html) ============ */
/* was an inline <style> in footer.html — moved here per this file's own rule
   that all styling lives in style.css, not scattered inline tags */
#form-buttons .vibor {
  width: calc(2.75 * var(--base-spacing));
  height: calc(2.75 * var(--base-spacing));
}

/* ============ INSTANT PREVIEW TOGGLE (#togglePreview, see footer.html) ============ */
/* was an inline <style> in footer.html — moved here per this file's own rule
   that all styling lives in style.css, not scattered inline tags. Floats the
   toggle button up next to the fieldset's own legend instead of it falling
   into normal flow above the toolbar. */
#togglePreview {
  position: absolute;
  top: calc(0.5 * var(--base-spacing));
  right: calc(3 * var(--base-spacing));
  z-index: 1;
}

/* compact ghost variant of .button: the legend bar is a quiet label row, a
   full banner-gradient button there fights the heading above it */
#togglePreview .button {
  padding: calc(0.25 * var(--base-spacing)) calc(1.25 * var(--base-spacing));
  font-size: 0.64286rem;
  color: var(--color-heading);
  background: transparent;
  border-color: rgba(var(--color-border-rgb), 0.6);
  box-shadow: none;
}

#togglePreview .button:hover {
  background: rgba(var(--color-accent-rgb), 0.15);
  border-color: var(--color-accent);
  transform: none;
}

/* ============ SCROLL NAV (jump to top / bottom) ============ */
.scroll-nav {
  position: fixed;
  right: calc(3 * var(--base-spacing));
  bottom: calc(3 * var(--base-spacing));
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: calc(1 * var(--base-spacing));
}

.scroll-nav-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: calc(5 * var(--base-spacing));
  height: calc(5 * var(--base-spacing));
  background: linear-gradient(180deg, var(--color-banner-bg-light) 0%, var(--color-banner-bg) 100%);
  border: 1px solid var(--color-accent);
  border-radius: 4px;
  box-shadow: 0 2px 6px rgba(var(--color-shadow-rgb), 0.3);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s, border-color 0.2s;
}

.scroll-nav-btn.is-visible {
  opacity: 1;
  pointer-events: auto;
}

.scroll-nav-btn::before {
  font: normal 900 16px "Font Awesome 6 Free";
  color: var(--color-accent);
  transition: color 0.2s;
}

.scroll-nav-btn:hover,
.settings-drawer-toggle.is-open {
  border-color: var(--color-accent-hover);
}

.scroll-nav-btn:hover::before,
.settings-drawer-toggle.is-open::before {
  color: var(--color-accent-bright);
}

.scroll-nav-btn:focus-visible,
.font-size-slider:focus-visible,
.settings-drawer-toggle:focus-visible,
.settings-drawer-close:focus-visible,
.news-item:focus-visible,
.chronicle-filter-input:focus-visible+.chronicle-chip {
  outline: 2px solid var(--color-accent-bright);
  outline-offset: 2px;
}

.scroll-nav-btn--up::before {
  content: "\f102";
  /* fa-angles-up */
}

.scroll-nav-btn--down::before {
  content: "\f103";
  /* fa-angles-down */
}

/* ============ SETTINGS DRAWER (topic reading settings, see footer.html) ============ */
/* Icon toggle sits fixed in its own top-right corner — right above where
   .settings-drawer itself pops out from, rather than down in the bottom-right
   .scroll-nav stack with the up/down arrows, so the trigger stays visually
   attached to the panel it opens. It borrows the .scroll-nav-btn box/icon
   look below (footer.html adds that class) and is always visible (no
   .is-visible scroll gating) — only its own position, ::before content and
   .is-open highlight are specific to it. .settings-drawer's own top offset
   is pushed down to clear this button (see below). */
.settings-drawer-toggle {
  position: fixed;
  top: calc(3 * var(--base-spacing));
  right: calc(3 * var(--base-spacing));
  z-index: 50;
}

.settings-drawer-toggle::before {
  content: "\f031";
  /* fa-font */
}

.settings-drawer {
  position: fixed;
  /* toggle sits at top: 3 * base-spacing, is 5 * base-spacing tall — this
     clears it with a 1 * base-spacing gap instead of sitting underneath it */
  top: calc(9 * var(--base-spacing));
  right: 0;
  z-index: 45;
  width: min(300px, 88vw);
  background: var(--color-panel-bg);
  border: 1px solid var(--color-accent);
  border-right: none;
  border-radius: calc(0.75 * var(--base-spacing)) 0 0 calc(0.75 * var(--base-spacing));
  box-shadow: -4px 4px 16px rgba(var(--color-shadow-rgb), 0.35);
  transform: translateX(100%);
  transition: transform 0.3s ease;
  overflow: hidden;
}

.settings-drawer.is-open {
  transform: translateX(0);
}

.settings-drawer-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: calc(1 * var(--base-spacing));
  padding: calc(1.25 * var(--base-spacing)) calc(2 * var(--base-spacing));
  background: linear-gradient(180deg, var(--color-banner-bg-light) 0%, var(--color-banner-bg) 100%);
  border-bottom: 1px solid var(--color-accent);
}

.settings-drawer-header span {
  font-family: var(--font-heading);
  font-size: 0.85714rem;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--color-text-on-dark);
}

.settings-drawer-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: calc(2.5 * var(--base-spacing));
  height: calc(2.5 * var(--base-spacing));
  background: none;
  border: none;
  color: var(--color-text-on-dark-muted);
  cursor: pointer;
  transition: color 0.2s;
}

.settings-drawer-close::before {
  font: normal 900 0.85714rem "Font Awesome 6 Free";
  content: "\f00d";
  /* fa-xmark */
}

.settings-drawer-close:hover {
  color: var(--color-accent-bright);
}

.settings-drawer-body {
  padding: calc(2 * var(--base-spacing));
}

.font-size-control {
  display: flex;
  align-items: center;
  gap: calc(1 * var(--base-spacing));
}

.font-size-control-icon {
  font-family: var(--font-heading);
  font-weight: 600;
  line-height: 1;
  color: var(--color-accent);
  user-select: none;
}

.font-size-control-icon--dec {
  font-size: 0.71429rem;
}

.font-size-control-icon--inc {
  font-size: 1.14286rem;
}

.font-size-control-value {
  min-width: calc(4 * var(--base-spacing));
  font-family: var(--font-heading);
  font-size: 0.71429rem;
  letter-spacing: 0.5px;
  color: var(--color-text-on-dark-subtle);
  text-align: center;
  user-select: none;
}

/* Native range input, restyled per-browser (WebKit/Blink + Firefox need
   separate pseudo-elements — there's no shared shorthand) to match the site's
   gold-on-dark theme instead of the OS default slider. */
.font-size-slider {
  -webkit-appearance: none;
  appearance: none;
  width: calc(11 * var(--base-spacing));
  height: calc(0.5 * var(--base-spacing));
  background: rgba(var(--color-accent-rgb), 0.3);
  border-radius: calc(0.25 * var(--base-spacing));
  outline: none;
  cursor: pointer;
}

.font-size-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: calc(2.25 * var(--base-spacing));
  height: calc(2.25 * var(--base-spacing));
  border-radius: 50%;
  background: linear-gradient(180deg, var(--color-accent-light), var(--color-accent));
  box-shadow: 0 1px 3px rgba(var(--color-shadow-rgb), 0.3);
  cursor: pointer;
  transition: transform 0.15s;
}

.font-size-slider::-moz-range-track {
  background: rgba(var(--color-accent-rgb), 0.3);
  height: calc(0.5 * var(--base-spacing));
  border-radius: calc(0.25 * var(--base-spacing));
}

.font-size-slider::-moz-range-thumb {
  width: calc(2.25 * var(--base-spacing));
  height: calc(2.25 * var(--base-spacing));
  border: none;
  border-radius: 50%;
  background: linear-gradient(180deg, var(--color-accent-light), var(--color-accent));
  box-shadow: 0 1px 3px rgba(var(--color-shadow-rgb), 0.3);
  cursor: pointer;
  transition: transform 0.15s;
}

.font-size-slider:hover::-webkit-slider-thumb,
.font-size-slider:hover::-moz-range-thumb {
  transform: scale(1.12);
}

/* ============ MOBILE (&#8804;640px) ============ */
@media (max-width: 640px) {
  .hero {
    height: auto;
    overflow: visible;
  }

  /* the vignette exists so overlaid title text stays legible on the image —
     on mobile the title sits in normal flow below the image, not overlaid,
     and .hero grows to auto height around the stacked content below it, so
     this inset:0 gradient would otherwise stretch down and shadow the
     highlights section too */
  .hero::after {
    display: none;
  }

  .hero img {
    height: 200px;
    width: 100%;
    object-fit: cover;
    display: block;
  }

  .title-overlay {
    position: relative;
    padding: calc(3 * var(--base-spacing)) 0 calc(10 * var(--base-spacing));
    top: 0;
  }

  .title-overlay h1 {
    font-size: 1.71429rem;
    letter-spacing: 2px;
  }

  .title-overlay .subtitle {
    font-size: 0.85714rem;
    margin-top: calc(0.5 * var(--base-spacing));
  }

  .title-meta {
    font-size: 0.64286rem;
    letter-spacing: 2px;
    margin-top: calc(0.75 * var(--base-spacing));
  }

  /* the 4-card overlay is too cramped on a short mobile hero — pull it out of the
     image entirely and let it run as a solid strip below: the first 3 cards
     (players/post/episode) become a carousel with nav arrows and dots, and
     the queue card stretches full-width underneath it */
  .highlights {
    position: static;
    flex-direction: column;
    background: var(--color-surface-raised);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-top: 2px solid var(--color-border);
  }

  .highlights-carousel {
    display: block;
    width: 100%;
  }

  .highlights-carousel-viewport {
    position: relative;
  }

  .highlights-carousel-track {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }

  .highlights-carousel-track::-webkit-scrollbar {
    display: none;
  }

  /* the extra side padding (beyond .highlight-card's own) keeps text clear of
     the overlaid nav arrows — each is 3*base wide, offset 0.5*base from the edge */
  .highlights-carousel-track .highlight-card {
    flex: 0 0 100%;
    scroll-snap-align: start;
    padding-left: calc(4 * var(--base-spacing));
    padding-right: calc(4 * var(--base-spacing));
  }

  /* z-index pins these above the track's own content — .highlight-card
     descendants may establish their own positioned/stacking context, which
     would otherwise let card content paint over these sibling buttons */
  .carousel-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    z-index: 2;
    top: 50%;
    transform: translateY(-50%);
    width: calc(3 * var(--base-spacing));
    height: calc(3 * var(--base-spacing));
    border: 1px solid rgba(var(--color-accent-rgb), 0.4);
    border-radius: 50%;
    background: rgba(var(--color-bg-rgb), 0.7);
    color: var(--color-accent);
    font-size: 1.14286rem;
    line-height: 1;
    cursor: pointer;
  }

  .carousel-nav--prev {
    left: calc(0.5 * var(--base-spacing));
  }

  .carousel-nav--next {
    right: calc(0.5 * var(--base-spacing));
  }

  .carousel-nav:disabled {
    opacity: 0.35;
    cursor: default;
  }

  .carousel-dots {
    display: flex;
    justify-content: center;
    gap: calc(0.5 * var(--base-spacing));
    padding: calc(0.75 * var(--base-spacing)) 0;
  }

  .carousel-dot {
    width: calc(0.75 * var(--base-spacing));
    height: calc(0.75 * var(--base-spacing));
    border: none;
    border-radius: 50%;
    padding: 0;
    background: rgba(var(--color-accent-rgb), 0.3);
    cursor: pointer;
  }

  .carousel-dot--active {
    background: var(--color-accent);
  }

  .highlight-card {
    border-right: none;
    border-bottom: none;
    padding: calc(1.25 * var(--base-spacing)) calc(2 * var(--base-spacing));
  }

  .highlight-card--queue {
    width: 100%;
    border-top: 1px solid rgba(var(--color-accent-rgb), 0.2);
  }

  .players-row {
    flex-wrap: wrap;
    justify-content: center;
    gap: calc(1 * var(--base-spacing)) calc(1.5 * var(--base-spacing));
  }

  .post-excerpt,
  .episode-desc {
    -webkit-line-clamp: 2;
  }

  #pun-navlinks {
    padding: calc(1 * var(--base-spacing)) calc(1.75 * var(--base-spacing));
  }

  #pun-navlinks a {
    font-size: 0.71429rem;
  }

  .status-avatar {
    width: calc(3.5 * var(--base-spacing));
    height: calc(3.5 * var(--base-spacing));
  }

  .punbb {
    border-left: none;
    border-right: none;
    box-shadow: none;
  }

  /* !important here: #pun-crumbs2's own padding shorthand is set at desktop
     width, so a normal-weight override can never win regardless of specificity */
  .punbb .crumbs,
  #pun-crumbs2 {
    padding-left: calc(1.75 * var(--base-spacing)) !important;
    padding-right: calc(1.75 * var(--base-spacing)) !important;
  }

  /* the desktop "counts next to title" trick relies on fixed pixel offsets that
     only work at desktop widths — abandon it here and fall back to simple stacked flow */
  .category tbody tr {
    flex-wrap: wrap;
    padding: calc(1.5 * var(--base-spacing)) calc(1.75 * var(--base-spacing));
  }

  .category .tclcon {
    max-width: 100%;
  }

  .category .tclcon h3 {
    max-width: 100%;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
  }

  .category .forum-meta-row {
    margin-top: calc(0.75 * var(--base-spacing));
  }

  .category .forum-meta-row td.tc2 {
    padding-right: calc(1.25 * var(--base-spacing));
  }

  .category .forum-meta-row td.tc3 {
    padding: 0 calc(1.25 * var(--base-spacing));
  }

  .category .forum-meta-row td.tcr {
    flex: 1 1 100%;
    padding-top: calc(1 * var(--base-spacing));
    border-top: 1px dashed rgba(var(--color-border-rgb), 0.3);
  }

  /* .forum mobile: the same boxed-card treatment as the usertable/online-list
     cards above (background/border/radius per row) instead of the bare
     divider list this table used until now, so every card list on the site
     reads as one system. Title/username are unbounded length, but the
     counts and pagination next to them are short and effectively
     fixed-width — forcing the unbounded fields (tcl, tcr) onto their own
     full line means the fixed pair (tc2/tc3) always lands together on the
     line between them, regardless of how long the title is. */
  .forum tbody tr {
    padding: calc(1.25 * var(--base-spacing)) calc(1 * var(--base-spacing));
    margin-bottom: calc(1 * var(--base-spacing));
    background: rgba(var(--color-border-rgb), 0.06);
    border: 1px solid rgba(var(--color-border-rgb), 0.25);
    border-radius: calc(0.5 * var(--base-spacing));
  }

  .forum tbody tr:last-child {
    margin-bottom: 0;
  }

  /* title+started-by is this card's identity line — same divider treatment
     as .usertable's username row */
  .forum td.tcl {
    flex: 1 1 100%;
    padding-bottom: calc(0.75 * var(--base-spacing));
    margin-bottom: calc(0.5 * var(--base-spacing));
    border-bottom: 1px solid rgba(var(--color-border-rgb), 0.25);
  }

  /* fixed-length stat pair, side by side as label:value — the border-right
     divider from the desktop skin only made sense flush inside a full row;
     floating alone here it read as a stray mark, so it's dropped in favor
     of the same ::before label convention used throughout the other cards */
  .forum td.tc2,
  .forum td.tc3 {
    flex: 1 1 50%;
    width: auto;
    box-sizing: border-box;
    border-right: none;
    text-align: left;
  }

  .forum td.tc2::before {
    content: "Îòâåòîâ: ";
    color: var(--color-link);
  }

  .forum td.tc3::before {
    content: "Ïðîñìîòðîâ: ";
    color: var(--color-link);
  }

  /* search/active-topics remaps the columns: tc2 is the source forum's name
     (its own desktop rule pins it to 120px for the inline ellipsis look,
     which isn't needed once it has the full card width to itself), tc3 is
     a reply count rather than a view count */
  #pun-searchtopics .forum td.tc2 {
    width: auto;
  }

  #pun-searchtopics .forum td.tc2::before {
    content: "Ôîðóì: ";
  }

  #pun-searchtopics .forum td.tc3::before {
    content: "Îòâåòîâ: ";
  }

  .forum td.tcr {
    flex: 1 1 100%;
    width: auto;
    padding-top: calc(0.75 * var(--base-spacing));
    margin-top: calc(0.5 * var(--base-spacing));
    border-top: 1px solid rgba(var(--color-border-rgb), 0.25);
  }

  /* the overlaid title/toolbar row has no room to share a line at mobile widths —
     drop the overlay and stack the toolbar back under the title */
  .punbb #pun-main>h1 {
    padding: calc(1.25 * var(--base-spacing)) calc(1.5 * var(--base-spacing));
  }

  .punbb #pun-main>h1 span {
    font-size: 0.92857rem;
    letter-spacing: 2px;
  }

  .punbb #pun-main>h1+.linkst {
    grid-row: 2;
    position: static;
    z-index: auto;
    justify-content: center;
    text-align: center;
    border-bottom: 1px dashed rgba(var(--color-border-rgb), 0.3);
    color: var(--color-text-muted);
  }

  .punbb #pun-main>h1+.linkst .pagelink strong {
    color: var(--color-heading);
  }

  /* buttons are off the banner and back on the panel background here — restore
     the normal button chrome instead of the flush-embedded header treatment */
  .punbb #pun-main>h1+.linkst .postlink {
    margin-left: 0;
    justify-content: center;
    gap: calc(1 * var(--base-spacing));
  }

  .punbb #pun-main>h1+.linkst .postlink a {
    background: linear-gradient(180deg, var(--color-banner-bg-light) 0%, var(--color-banner-bg) 100%);
    border: 1px solid var(--color-accent);
    border-radius: calc(0.5 * var(--base-spacing));
    padding: calc(0.5 * var(--base-spacing)) calc(1.25 * var(--base-spacing));
    font-size: 0.78571rem;
    color: var(--color-text-on-dark);
  }

  .punbb #pun-main>h1+.linkst .postlink span+span {
    border-left: none;
  }

  /* stack pagelink/buttons/subscribe instead of the lopsided line-break wrap
     that flex-wrap alone produces once the row no longer fits. align-items
     has to be overridden too — the base rule's "center" carries into column
     mode as the cross axis (width), which would keep every row shrunk to
     content instead of letting them stretch full-width below */
  .punbb .linksb,
  .punbb .linkst {
    flex-direction: column;
    align-items: stretch;
    text-align: center;
    padding: calc(1 * var(--base-spacing)) calc(1.5 * var(--base-spacing));
  }

  /* the forum platform's own stylesheet fixes .postlink.long at width: 30em
     for the desktop two-button row — that overrides our percentage widths
     below and eats the right-side padding once buttons stack, so pin it
     back to the container's width here */
  .punbb .linksb .postlink,
  .punbb .linkst .postlink {
    flex-wrap: wrap;
    width: 100%;
  }

  /* postlink's buttons are each wrapped in their own <span> — that span, not
     the <a>, is the flex item postlink lays out, so it has to stretch too.
     the forum platform's own stylesheet puts a right-padding spacer on the
     first span (for the desktop side-by-side layout) — zero it out here since
     stacked buttons space themselves via the postlink gap instead */
  .punbb .linkst .postlink>span,
  .punbb .linksb .postlink>span {
    width: 100%;
    padding: 0;
  }

  .punbb .linksb .postlink a,
  .punbb .linkst .postlink a,
  .punbb .linksb .subscribelink a {
    box-sizing: border-box;
    width: 100%;
  }

  /* .font-size-control (see footer.html) isn't wrapped in its own <span> like
     the "Îòâåòèòü" link above — it's the plain div the widget script
     appends — so it needs the same full-width stacking spelled out directly
     instead of inheriting it from the >span/a rules above. */
  .punbb .linksb .font-size-control {
    box-sizing: border-box;
    width: 100%;
    justify-content: center;
  }

  /* profile sidebar stacks above the post body instead of sitting beside it */
  .messages-container .post {
    margin: calc(2 * var(--base-spacing)) 0 0;
  }

  .topic .post .container,
  .messages-container .post .container {
    flex-direction: column;
  }

  .post-author {
    flex: 0 0 auto;
    width: 100%;
    box-sizing: border-box;
    border-right: none;
    border-bottom: 2px solid var(--color-accent);
    padding: calc(0.75 * var(--base-spacing)) calc(1.25 * var(--base-spacing));
    text-align: left;
    top: 0;
  }

  /* the scrim/border lives on the list box itself (colors.css), so on mobile the
     grid goes on .post-author ul directly instead of dissolving it away —
     letting the li's be placed explicitly instead of stacking in DOM order */
  .post-author ul {
    display: grid;
    grid-template-columns: 64px 1fr;
    justify-content: start;
    gap: var(--base-spacing);
    align-items: center;
    position: relative;
    z-index: 2;
  }

  .post-author li {
    padding: calc(0.125 * var(--base-spacing)) 0;
  }

  /* positive offsets from the <ul> itself: the desktop padding-compensated
     offsets pushed the icon above/outside the card here, clipping it */
  .post-author .pa-online {
    top: calc(1.375 * var(--base-spacing));
    right: calc(0.25 * var(--base-spacing));
  }

  /* row 1-2: username, title stacked plainly beside the avatar (no badge/pill) */
  .post-author .pa-author {
    grid-column: 2;
    grid-row: 1;
  }

  .post-author .pa-title {
    grid-column: 2;
    grid-row: 2;
    width: auto;
    margin: 0;
    border-top: none;
    padding-top: 0;
  }

  /* avatar (real or placeholder) sits beside username + title */
  .post-author .pa-avatar,
  .post-author.no-avatar::before {
    grid-column: 1;
    grid-row: 1 / 3;
    align-self: start;
  }

  .post-author .pa-avatar img,
  .post-author.no-avatar::before {
    max-width: 64px;
    margin: 0;
  }

  .topic .post h3,
  .messages-container .post h3 {
    padding-left: calc(3 * var(--base-spacing));
  }

  /* fld3 doesn't fit this compact layout */
  .post-author .pa-fld3 {
    display: none;
  }

  /* plashka banner dropped on mobile, both here and on the profile view
     page's card — the shared class covers both */
  .profile-card-plashka {
    display: none;
  }

  /* .pa-stats (wrapper added by JS) spans the full row below avatar/title as
     one bar, splitting evenly across whichever of these fields this user has set */
  .post-author .pa-stats {
    order: 1;
    grid-column: 1 / -1;
    margin: 0;
  }

  .post-author .pa-stats>li {
    padding: calc(0.5 * var(--base-spacing)) calc(0.5 * var(--base-spacing)) calc(0.375 * var(--base-spacing));
    text-align: center;
  }

  .post-author .pa-positive {
    order: 1;
    grid-column: 1 / -1;
    justify-self: start;
    box-sizing: border-box;
    width: auto;
    margin: 0;
    padding: calc(0.5 * var(--base-spacing)) calc(0.5 * var(--base-spacing)) calc(0.375 * var(--base-spacing));
    background: var(--pa-panel-bg);
    border: var(--pa-panel-border);
    border-radius: var(--pa-panel-radius);
  }

  .post-author .pa-positive .fld-name {
    display: block;
    font-size: 0;
  }

  .post-author .pa-positive .fld-name a::before {
    font: normal 900 10px "Font Awesome 6 Free";
    color: color-mix(in srgb, var(--pa-neutral) 85%, transparent);
    content: "\f164";
    /* fa-thumbs-up */
  }

  /* everything else keeps stacking below, one full-width row each, after the icon stats */
  .post-author .pa-reg,
  .post-author .pa-invites,
  .post-author .pa-time-visit,
  .post-author .pa-ip {
    order: 2;
    grid-column: 1 / -1;
    width: auto;
    margin: 0;
  }


  .post-links {
    flex-basis: 100%;
    margin-left: 0;
  }

  .post-content,
  .post-rating,
  .post-links {
    padding-left: calc(2 * var(--base-spacing));
    padding-right: calc(2 * var(--base-spacing));
  }

  #post-form legend,
  #post-form .fs-box,
  #post-form .formsubmit,
  #post-form>p {
    padding-left: calc(1.75 * var(--base-spacing));
    padding-right: calc(1.75 * var(--base-spacing));
  }

  /* the legend's instruction text can wrap to two lines at mobile widths,
     so the absolute pin from desktop would cover it — drop the toggle into
     normal flow below the legend instead. */
  #togglePreview {
    position: static;
    display: block;
    margin: calc(0.5 * var(--base-spacing)) calc(1.75 * var(--base-spacing)) 0;
  }

  #post-form .formsubmit,
  #post-form>p {
    flex-direction: column;
    align-items: stretch;
  }

  /* auto-margin push only makes sense inline, next to the buttons row — stacked
     column layout already puts it on its own line */
  #post-form>p>.checkfield {
    margin-left: 0;
  }

  /* submit + preview stack full-width instead of sitting side by side */
  #post-form>p>span:first-child {
    flex-direction: column;
    align-items: stretch;
  }

  #post-form>p>span:first-child .button {
    width: 100%;
    text-align: center;
  }

  /* the fixed 32px-per-button offsets used to line panels up with their desktop
     toolbar column don't hold once the toolbar wraps onto multiple lines — drop
     back into normal flow, directly under the toolbar, instead */
  #post-form .container[id$="-area"] {
    position: static !important;
    margin-top: calc(1 * var(--base-spacing));
    max-width: 100%;
    box-sizing: border-box;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }

  /* bigger tap targets than the 32px desktop row */
  #form-buttons td,
  #tags>table td {
    flex-basis: 40px;
    height: 40px;
  }

  #form-buttons td::before,
  #tags>table td::before {
    font-size: 1.14286rem;
  }

  /* ---- userlist ---- */
  .formal .fs-box.inline {
    flex-direction: column;
  }

  .formal .inputfield input[type="text"],
  .formal .selectfield select {
    width: 100%;
  }

  #messages td.pmtc22 {
    white-space: normal;
  }

  /* ---- plain data tables (online users list + member list + respect
     list): real multi-column grids don't fit narrow screens, so all three
     stack each row's cells identically, with a hardcoded label per column
     standing in for the now-hidden header ---- */
  .online-list thead,
  .usertable thead,
  #pun-respect thead {
    display: none;
  }

  .online-list table,
  .online-list tbody,
  .online-list tr,
  .online-list td,
  .usertable table,
  .usertable tbody,
  .usertable tr,
  .usertable td,
  #pun-respect table,
  #pun-respect tbody,
  #pun-respect tr,
  #pun-respect td {
    display: block;
  }

  .online-list tr,
  .usertable tr,
  #pun-respect tr {
    padding: calc(1 * var(--base-spacing)) 0;
  }

  .online-list td,
  .usertable td,
  #pun-respect td {
    padding: calc(0.25 * var(--base-spacing)) 0;
    text-align: left;
    white-space: normal;
  }

  /* .tc3 carries `white-space: nowrap` + `text-align: right` in the desktop
     table skin (style.css ~1024) at higher specificity than the reset above,
     so it survives into the stacked mobile layout and forces a horizontal
     scrollbar on the whole table — reset it explicitly here. #pun-respect's
     equivalent right-aligned column is .tcr, not .tc3 (see desktop skin). */
  .online-list td.tc3,
  .usertable td.tc3,
  #pun-respect td.tcr {
    text-align: left;
    white-space: normal;
  }

  /* the forum platform's own extra.css pins this column to 25% width via
     `#pun-online .tcl.action` (an ID selector we don't control, at higher
     specificity than a plain class rule) — that 25% is fine as a table
     column, but on the stacked mobile card it squeezes "Ñìîòðèò ñïèñîê
     àêòèâíûõ ïîëüçîâàòåëåé" down to ~84px, wrapping one word per line.
     `#pun-online` here matches it back to reclaim full width; the extra
     .action class pushes the class-count tiebreak in our favor too. */
  #pun-online .online-list td.action {
    width: 100%;
  }

  /* ---- boxed-card shell shared by all three tables: a bare stacked list
     reads as one continuous run with no boundary between rows, so every row
     becomes its own card. .online-list/.usertable additionally divide off a
     username heading; #pun-respect has no single identity column to lead
     with (see labels below), so it skips that divider and labels every
     field instead. usertable's extra stat fields need the 2-col grid added
     further below. ---- */
  .online-list tbody tr,
  .usertable tbody tr,
  #pun-respect tbody tr {
    padding: calc(1.25 * var(--base-spacing)) calc(1 * var(--base-spacing));
    margin-bottom: calc(1 * var(--base-spacing));
    background: rgba(var(--color-border-rgb), 0.06);
    border: 1px solid rgba(var(--color-border-rgb), 0.25);
    border-radius: calc(0.5 * var(--base-spacing));
  }

  .online-list tbody tr:last-child,
  .usertable tbody tr:last-child,
  #pun-respect tbody tr:last-child {
    margin-bottom: 0;
  }

  .online-list td.username,
  .usertable tbody td.username {
    padding-bottom: calc(0.75 * var(--base-spacing));
    margin-bottom: calc(0.25 * var(--base-spacing));
    border-bottom: 1px solid rgba(var(--color-border-rgb), 0.25);
  }

  .online-list td.action::before {
    content: "Äåéñòâèå: ";
    color: var(--color-link);
  }

  .online-list td.tc3::before {
    content: "Âðåìÿ: ";
    color: var(--color-link);
  }

  /* #pun-respect column labels — table has no single self-explanatory
     identity column (unlike a username), so every field gets one, including
     the first. The empty-state row ("Ê Õ ïîêà íå âûðàæàëè...") is a single
     td.tcl with colspan, not a real data row — :not([colspan]) keeps the
     label off it. */
  #pun-respect td.tcl:not([colspan])::before {
    content: "Êîãäà: ";
    color: var(--color-link);
  }

  #pun-respect td.tc2::before {
    content: "×òî: ";
    color: var(--color-link);
  }

  #pun-respect td.tc3:not(.tc4)::before {
    content: "Êòî: ";
    color: var(--color-link);
  }

  #pun-respect td.tc4::before {
    content: "Çà ÷òî: ";
    color: var(--color-link);
  }

  #pun-respect td.tcr::before {
    content: "Â òåìå: ";
    color: var(--color-link);
  }

  .usertable td.user_title::before {
    content: "Ñòàòóñ: ";
    color: var(--color-link);
  }

  .usertable td.relation::before {
    content: "Óâàæåíèå: ";
    color: var(--color-link);
  }

  .usertable td.num_posts::before {
    content: "Ñîîáùåíèé: ";
    color: var(--color-link);
  }

  .usertable td.registered::before {
    content: "Çàðåãèñòðèðîâàí: ";
    color: var(--color-link);
  }

  .usertable td.last_visit::before {
    content: "Áûë: ";
    color: var(--color-link);
  }

  /* ---- usertable member cards: on top of the shared card shell above,
     the five stat fields (status/relation/posts/registered/last_visit)
     read better as a compact label:value grid than as five full-width
     lines each mostly empty space — .online-list has no equivalent since
     its 3 columns are already a single line of running text (no avatar,
     no stat block), so it stays with plain block stacking.
     `tbody` in every selector here matches the desktop
     `.usertable tbody tr`/`tbody tr:hover` rules' specificity (0,2,0) so
     these mobile overrides actually win by source order instead of losing
     to the desktop rule despite the media query. ---- */
  .usertable tbody tr {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: calc(1 * var(--base-spacing));
    row-gap: calc(0.5 * var(--base-spacing));
  }

  .usertable tbody td {
    display: flex;
    justify-content: flex-start;
    align-items: baseline;
    gap: calc(0.5 * var(--base-spacing));
    padding: 0;
  }

  .usertable tbody td::before {
    flex: 0 0 auto;
  }

  /* a long status/group name would otherwise wrap the "Ñòàòóñ: " label onto
     its own line inside the narrow grid column — truncate it instead. Not a
     flex row like the other stat cells: text-overflow:ellipsis needs one
     plain text flow (label + value) to clip as a unit, which a flex layout
     (label and value as separate boxes) can't do */
  .usertable tbody td.user_title {
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* the date value itself is short but "2026-08-30" carries soft-wrap
     opportunities at each hyphen, so normal wrapping still breaks it
     mid-date once label + value don't both fit on one line */
  .usertable tbody td.registered {
    white-space: nowrap;
  }

  .usertable tbody td.username {
    grid-column: 1 / -1;
    justify-content: flex-start;
    align-items: center;
    gap: calc(1 * var(--base-spacing));
  }

  .usertable .avatar-image {
    width: 40px !important;
    height: 40px !important;
  }

  .scroll-nav {
    right: calc(1.5 * var(--base-spacing));
    bottom: calc(1.5 * var(--base-spacing));
  }

  /* top: 1.5 * base-spacing (matching .scroll-nav's own mobile inset) would
     sit under the header bar's right-side controls (user menu, mail) at this
     width — push it below the header instead */
  .settings-drawer-toggle {
    top: calc(8 * var(--base-spacing));
    right: calc(1.5 * var(--base-spacing));
  }

  /* toggle shrinks to 4.5 * base-spacing tall at this width (.scroll-nav-btn
     override below) and sits at top: 8 instead of 3 — keep the same 1 *
     base-spacing gap under it */
  .settings-drawer {
    top: calc(13.5 * var(--base-spacing));
  }

  /* below ~1082px the parchment panel runs edge-to-edge, so there's no dark
     margin for the buttons to float in — they sit over live content instead.
     Ease that with the same translucent-blur treatment already used for
     overlays on the hero (.highlights), instead of a solid opaque block. */
  .scroll-nav-btn {
    width: calc(4.5 * var(--base-spacing));
    height: calc(4.5 * var(--base-spacing));
    background: rgba(var(--color-bg-rgb), 0.55);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
  }
}

#pun-index #pun-main h1,
.usertable h2,
.forum h2,
#pun-viewtopic .topic h2,
.translatelink,
#pun-live-rusff {
  display: none !important;
}

/* ============ WORLD TIMELINE (articles/timeline.html) ============
   Standalone page, not a per-country lore article — reuses .lore-article
   for the paper/type shell and the .lore-tab-input/.lore-tabs/.lore-tab/
   .lore-panels/.lore-panel radio mechanism verbatim (capped at 6, see
   colors.css; the era labels keep .lore-tab for its checked color, hover
   and focus outline). Top to bottom it repeats the forum index's own
   stack: a dark plate with a centered title (the index hero), a night strip
   of era columns (the highlights strip) whose rail fills up to the picked
   era like the launch bar, then the scope filter as hairline pills on the
   raised band (the news strip). Below that, on parchment, events hang off
   a spine on diamond markers in their scope's color. Everything is rendered
   from JSON by the inline script in articles/timeline.html (post it
   wrapped in [html]...[/html]). Colors: --tl-* vars in colors.css; the
   hero title vars and the art fade are shared with .lore-intro there. */

/* art comes from an inline background-image on the plate, the same way
   .lore-illustration takes it; until then the gradient fills in */
.chronicle-plate {
  position: relative;
  margin: calc(-1 * var(--lc-pad-y)) calc(-1 * var(--lc-pad-x)) 0;
  padding: calc(22 * var(--base-spacing)) var(--lc-pad-x) calc(3 * var(--base-spacing));
  text-align: center;
  background: linear-gradient(180deg, var(--color-banner-bg-dark), var(--color-bg)) center 30% / cover no-repeat;
  border-top: 1px solid var(--color-accent);
}

/* placeholder text in the plate's corner — delete the span once the
   plate gets its background-image */
.chronicle-art-note {
  position: absolute;
  top: calc(2 * var(--base-spacing));
  right: var(--lc-pad-x);
  max-width: 50%;
  font-size: 0.85714rem;
  font-style: italic;
  text-align: right;
  color: var(--color-text-on-dark-subtle);
  opacity: 0.7;
}

/* centered like the index title, so the gold rule fades out both ways
   instead of running off to the right as in the country hero */
.chronicle-plate h1 {
  margin: 0 0 calc(1.5 * var(--base-spacing));
  padding-bottom: calc(2 * var(--base-spacing));
  letter-spacing: 1px;
  background: linear-gradient(90deg, transparent 20%, var(--color-accent) 50%, transparent 80%) no-repeat center bottom / 100% 1px;
}

/* era strip: equal columns, not widths proportional to each era's span —
   the present is thirty years against the Exodus's three centuries and
   would shrink to a sliver. The three-class prefixes out-rank the
   .lore-tabs band and .lore-tab underline/capitals from colors.css */
.lore-article .chronicle-eras {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  column-gap: 0;
  margin: 0 calc(-1 * var(--lc-pad-x));
  padding: 0;
  background: var(--color-bg);
  border: none;
}

.lore-article .chronicle-eras .chronicle-era-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: calc(0.5 * var(--base-spacing));
  margin: 0;
  padding: calc(4 * var(--base-spacing)) calc(2 * var(--base-spacing)) calc(2 * var(--base-spacing));
  border-bottom: none;
  letter-spacing: normal;
  text-transform: none;
  scroll-snap-align: start;
  transition: background-color 0.2s;
}

.lore-article .chronicle-eras .chronicle-era-card:hover {
  background: rgba(var(--color-accent-rgb), 0.06);
}

/* one rail segment per era: passed eras get the gold fill below, so the
   rail reads as how far into history the picked era sits. A dark cut at
   each segment's end keeps the eras apart inside the fill; on the empty
   rail it matches the rail itself */
.chronicle-era-card::before {
  --era-cut: linear-gradient(90deg, transparent calc(100% - 0.375 * var(--base-spacing)), var(--color-bg) 0);
  content: "";
  position: absolute;
  top: calc(1 * var(--base-spacing));
  left: 0;
  right: 0;
  height: calc(0.75 * var(--base-spacing));
  border-top: 1px solid rgba(var(--color-accent-rgb), 0.35);
  border-bottom: 1px solid rgba(var(--color-accent-rgb), 0.35);
  background: var(--era-cut), rgba(var(--color-bg-rgb), 0.6);
}

/* color comes from the label (.lore-tab: muted, accent-light once picked) */
.chronicle-era-name {
  font-size: 1.35714rem;
  line-height: 1.1;
}

.chronicle-era-range,
.chronicle-filter-label {
  font-size: 0.85714rem;
  font-style: italic;
  color: var(--color-text-on-dark-subtle);
}

.lore-article .chronicle-era-input:nth-of-type(1):checked~.chronicle-eras .chronicle-era-card:nth-of-type(-n+1)::before,
.lore-article .chronicle-era-input:nth-of-type(2):checked~.chronicle-eras .chronicle-era-card:nth-of-type(-n+2)::before,
.lore-article .chronicle-era-input:nth-of-type(3):checked~.chronicle-eras .chronicle-era-card:nth-of-type(-n+3)::before,
.lore-article .chronicle-era-input:nth-of-type(4):checked~.chronicle-eras .chronicle-era-card:nth-of-type(-n+4)::before,
.lore-article .chronicle-era-input:nth-of-type(5):checked~.chronicle-eras .chronicle-era-card:nth-of-type(-n+5)::before,
.lore-article .chronicle-era-input:nth-of-type(6):checked~.chronicle-eras .chronicle-era-card:nth-of-type(-n+6)::before {
  /* flat, not the launch bar's gradient — a gradient restarts in every
     segment and its seams read as dividers between eras */
  background: var(--era-cut), var(--color-accent);
  box-shadow: 0 0 var(--base-spacing) rgba(var(--color-accent-rgb), 0.45);
}

/* the diamond marks the picked era's own segment of the filled rail */
.lore-article .chronicle-era-input:nth-of-type(1):checked~.chronicle-eras .chronicle-era-card:nth-of-type(1)::after,
.lore-article .chronicle-era-input:nth-of-type(2):checked~.chronicle-eras .chronicle-era-card:nth-of-type(2)::after,
.lore-article .chronicle-era-input:nth-of-type(3):checked~.chronicle-eras .chronicle-era-card:nth-of-type(3)::after,
.lore-article .chronicle-era-input:nth-of-type(4):checked~.chronicle-eras .chronicle-era-card:nth-of-type(4)::after,
.lore-article .chronicle-era-input:nth-of-type(5):checked~.chronicle-eras .chronicle-era-card:nth-of-type(5)::after,
.lore-article .chronicle-era-input:nth-of-type(6):checked~.chronicle-eras .chronicle-era-card:nth-of-type(6)::after {
  content: "";
  position: absolute;
  top: calc(1.375 * var(--base-spacing));
  left: 50%;
  z-index: 1;
  width: calc(1.75 * var(--base-spacing));
  height: calc(1.75 * var(--base-spacing));
  transform: translate(-50%, -50%) rotate(45deg);
  border: 1px solid var(--color-accent-bright);
  background: var(--color-accent);
  box-shadow: 0 0 var(--base-spacing) rgba(var(--color-accent-rgb), 0.7);
}

.chronicle-filter {
  display: flex;
  align-items: center;
  gap: calc(1.5 * var(--base-spacing));
  margin: 0 calc(-1 * var(--lc-pad-x)) calc(4 * var(--base-spacing));
  padding: calc(1.5 * var(--base-spacing)) var(--lc-pad-x);
  background: var(--color-surface-raised);
  border-top: 1px solid rgba(var(--color-accent-rgb), 0.3);
  border-bottom: 1px solid var(--color-accent);
}

.chronicle-filter-chips {
  display: flex;
  flex-wrap: wrap;
  gap: calc(1 * var(--base-spacing));
}

/* same pill as .news-item (hover state shared with it, see NEWS STRIP) */
.chronicle-chip {
  display: inline-flex;
  align-items: center;
  gap: calc(1 * var(--base-spacing));
  padding: calc(0.625 * var(--base-spacing)) calc(2 * var(--base-spacing));
  border: 1px solid rgba(var(--color-accent-rgb), 0.25);
  border-radius: calc(4 * var(--base-spacing));
  font-size: 0.85714rem;
  color: var(--color-text-on-dark);
  cursor: pointer;
  transition: border-color 0.2s, background-color 0.2s;
}

/* each scope's seal: its own color (--item-accent, set inline by the
   script) under a gold rim, so Laterium's maroon still shows on the band */
.chronicle-chip[data-scope]::before {
  content: "";
  width: calc(1.25 * var(--base-spacing));
  height: calc(1.25 * var(--base-spacing));
  border-radius: 50%;
  background: var(--item-accent);
  border: 1px solid var(--color-accent-light);
}

.chronicle-filter-input:checked+.chronicle-chip {
  border-color: var(--color-accent);
  background: rgba(var(--color-accent-rgb), 0.16);
  color: var(--color-accent-bright);
}

.lore-article .chronicle-intro {
  margin: 0 0 calc(4 * var(--base-spacing));
  font-style: italic;
  opacity: 0.85;
}

.chronicle-detail-lead {
  font-family: var(--font-display);
  font-size: 1.57143rem;
  font-weight: 600;
  line-height: 1.25;
  color: var(--lc-accent-ink);
}

/* years sit in their own right-aligned column left of the spine, the way a
   chronicle's margin carries its dates */
.chronicle-timeline {
  --tl-year-col: calc(15 * var(--base-spacing));
  --tl-gutter: calc(3 * var(--base-spacing));
  display: grid;
  row-gap: calc(3.5 * var(--base-spacing));
  list-style: none;
  position: relative;
  margin: calc(4 * var(--base-spacing)) 0 0;
  padding: calc(1 * var(--base-spacing)) 0;
}

.chronicle-timeline::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--tl-year-col) + var(--tl-gutter));
  width: 1px;
  background: linear-gradient(to bottom, transparent, var(--color-border) 6%, var(--color-border) 94%, transparent);
}

/* --item-accent is set inline per event by the script; the ink variant is
   pulled toward night so light accents like Alsar's ochre read on paper */
.chronicle-item {
  --item-ink: color-mix(in srgb, var(--item-accent) 80%, var(--color-bg));
  position: relative;
  display: grid;
  grid-template-columns: var(--tl-year-col) minmax(0, 1fr);
  column-gap: calc(2 * var(--tl-gutter));
}

/* the grid display above would otherwise beat the filter's hidden attribute */
.chronicle-item[hidden] {
  display: none;
}

.chronicle-item::before {
  content: "";
  position: absolute;
  top: calc(0.75 * var(--base-spacing));
  left: calc(var(--tl-year-col) + var(--tl-gutter));
  width: calc(1.5 * var(--base-spacing));
  height: calc(1.5 * var(--base-spacing));
  transform: translateX(-50%) rotate(45deg);
  background: var(--item-accent);
  border: 1px solid var(--color-accent);
  box-shadow: 0 0 0 calc(0.375 * var(--base-spacing)) var(--lc-paper);
}

.chronicle-year,
.chronicle-item h4 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.42857rem;
  font-weight: 600;
  line-height: 1.1;
}

.chronicle-year {
  grid-row: 1 / span 3;
  justify-self: end;
  text-align: right;
  font-variant-numeric: oldstyle-nums;
  color: var(--item-ink);
}

.chronicle-scope-tag {
  margin: calc(0.25 * var(--base-spacing)) 0 calc(0.75 * var(--base-spacing));
  font-size: 0.78571rem;
  font-style: italic;
  color: var(--item-ink);
}

.chronicle-item p {
  font-size: 0.97143rem;
}

.chronicle-empty {
  font-style: italic;
  opacity: 0.65;
}

@media (max-width: 640px) {
  .chronicle-plate {
    padding-top: calc(14 * var(--base-spacing));
  }

  /* five columns don't fit a phone — the strip scrolls sideways like the
     index's news strip */
  .lore-article .chronicle-eras {
    grid-auto-columns: 42%;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
  }

  .chronicle-filter {
    flex-direction: column;
    align-items: flex-start;
    gap: calc(1 * var(--base-spacing));
  }

  /* the year column collapses: dates move above their title, the spine to
     the left edge */
  .chronicle-timeline {
    --tl-year-col: 0px;
    --tl-gutter: calc(1 * var(--base-spacing));
  }

  .chronicle-item {
    grid-template-columns: minmax(0, 1fr);
    padding-left: calc(3 * var(--base-spacing));
  }

  .chronicle-year {
    grid-row: auto;
    justify-self: start;
    text-align: left;
    font-size: 1.14286rem;
    margin-bottom: calc(0.25 * var(--base-spacing));
  }
}

/* ============ CHARACTER ROSTER (articles/characters.html) ============
   Standalone directory page, not a per-country lore article — reuses
   .lore-article for the shared paper/type/spacing shell plus .lore-tagline
   verbatim, but everything below is its own mechanism: native
   <details>/<summary> per territory instead of the radio-tab switcher (this
   list grows over time and isn't capped at 6 the way a country's tab set
   is), rendered from JSON by the inline script in articles/characters.html
   (see articles/characters-data.json — same fetch-and-render pattern as
   articles/timeline.html, different shape of data). Every territory uses
   the same --lc-accent — the LORE ARTICLE TEMPLATE's own default accent
   (colors.css), maroon, same as any country article that doesn't override
   it inline — rather than timeline.html's per-country --tl-* tinting: this
   page is a directory, not a country article, and a different color per
   entry here would read as ranking or grouping rather than just "this is
   the accent color the page uses".

   Inside a territory: one centered "masthead" card for the seat of power
   (.roster-hero, modelled on a reference roster's ruler card — office +
   title line + the court as a plain two-column list, not another boxed
   component), then a card grid of that territory's lands (.roster-lands),
   each land a bordered card listing its houses as plain rows — bold name,
   dash, role — instead of the old per-family crest-and-list block. Every
   person anywhere on the page (court, local ruler, house member) renders
   through the one .roster-row line so scanning the page means reading rows,
   not decoding three different card shapes. */

.roster-intro {
  margin: 0 0 calc(1 * var(--base-spacing));
}

/* the one place each status icon's meaning is spelled out in full — every
   .roster-status badge on the page is icon-only, so a reader learns what
   they mean once here instead of re-reading the same label on every name */
.roster-legend {
  display: flex;
  flex-wrap: wrap;
  gap: calc(0.5 * var(--base-spacing)) calc(2 * var(--base-spacing));
  margin: 0 0 calc(2 * var(--base-spacing));
}

.roster-legend-item {
  display: flex;
  align-items: center;
  gap: calc(0.5 * var(--base-spacing));
  margin: 0;
  font-size: 0.85714rem;
  opacity: 0.8;
}

.roster-territory {
  border: 1px solid var(--lc-paper-deep);
  border-left: 4px solid var(--lc-accent);
  border-radius: 0 4px 4px 0;
  margin-bottom: calc(1.25 * var(--base-spacing));
}

.roster-territory:last-child {
  margin-bottom: 0;
}

.roster-territory-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: calc(1.5 * var(--base-spacing));
  padding: calc(1 * var(--base-spacing)) calc(1.5 * var(--base-spacing));
  background: color-mix(in srgb, var(--lc-accent) 7%, var(--lc-paper));
  cursor: pointer;
  list-style: none;
}

.roster-territory[open]>.roster-territory-summary {
  border-bottom: 2px solid var(--lc-accent);
}

.roster-territory-summary::-webkit-details-marker {
  display: none;
}

.roster-territory-summary:focus-visible {
  outline: 2px solid var(--lc-accent);
  outline-offset: -2px;
}

.roster-territory-heading {
  display: flex;
  flex-direction: column;
  gap: calc(0.25 * var(--base-spacing));
}

/* the name is a real h2, so it picks up .lore-article h2's own flex layout
   and display type (colors.css) instead of reading as a plain text label */
.roster-territory .roster-territory-name {
  margin: 0;
  font-size: 1.15714rem;
  color: var(--lc-accent);
}

.roster-territory-count {
  font-size: 0.85714rem;
  opacity: 0.7;
}

/* plain triangle, rotates open — no chevron glyph needed from the icon font */
.roster-territory-toggle {
  flex: none;
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 6px solid var(--lc-accent);
  transition: transform 0.2s;
}

.roster-territory[open] .roster-territory-toggle {
  transform: rotate(180deg);
}

.roster-territory-body {
  padding: 0 calc(1.5 * var(--base-spacing)) calc(1.5 * var(--base-spacing));
}

/* shared small uppercase heading-font label — a seat's office title, a local
   ruler's title, same idiom as .lore-facts dt */
.roster-eyebrow {
  font-family: var(--font-heading);
  font-size: 0.9rem;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--lc-accent);
  font-weight: 600;
}

/* ---- seat of power: the Íàìåñòíèê's office and, where the territory has
   one, its hereditary ruling family (e.g. Äîì Îñíîâàòåëüíèöû in Àëüñàð) —
   the two sit side by side as equals (.roster-hero-heads--split) rather
   than one subordinate to the other, since neither outranks the other in
   how the territory is actually run. A territory with no ruling family
   (Ëàòåðèóì, Òÿíüñÿ) just gets the one head, full width. The court sits
   below, spanning both — it serves the office, not one house. ---- */
.roster-hero {
  text-align: center;
  margin: 0 calc(-1.5 * var(--base-spacing)) calc(1.5 * var(--base-spacing));
  padding: calc(1.5 * var(--base-spacing)) calc(3 * var(--base-spacing));
  background: color-mix(in srgb, var(--lc-accent) 6%, var(--lc-paper));
}

.roster-hero-heads {
  display: grid;
  grid-template-columns: 1fr;
}

.roster-hero-heads--split {
  grid-template-columns: 1fr 1fr;
}

.roster-hero-heads--split .roster-hero-head:first-child {
  padding-right: calc(1.5 * var(--base-spacing));
}

.roster-hero-heads--split .roster-hero-head:last-child {
  padding-left: calc(1.5 * var(--base-spacing));
  border-left: 1px solid var(--lc-paper-deep);
}

.roster-hero-emblem {
  width: 56px;
  height: 56px;
  margin: 0 auto calc(0.75 * var(--base-spacing));
  border: 2px solid var(--lc-accent);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.roster-hero-emblem i {
  font-size: 1.35rem;
  color: var(--lc-accent);
}

.roster-hero-title {
  margin: 0 0 calc(0.35 * var(--base-spacing));
}

.roster-hero-lede {
  margin: 0 0 calc(0.35 * var(--base-spacing));
  font-size: 1.05714rem;
  line-height: 1.5;
}

.roster-hero-lede strong {
  color: var(--lc-accent);
}

/* ---- hero card sections below the heads: currently just the court, but
   the same divider-plus-label treatment as a head's own label so the page
   keeps one visual idiom for "a labelled group of rows" ---- */
.roster-hero-section {
  margin-top: calc(1.25 * var(--base-spacing));
  padding-top: calc(1 * var(--base-spacing));
  border-top: 1px solid var(--lc-paper-deep);
  text-align: left;
}

.roster-hero-section-label {
  margin: 0 0 calc(0.5 * var(--base-spacing));
}

/* a plain two-column list of rows, not another boxed component — one card,
   one visual idiom, the rows do the rest */
.roster-hero-rows {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: calc(0.3 * var(--base-spacing)) calc(2 * var(--base-spacing));
  text-align: left;
}

/* inside a half-width head there's no room for two columns of rows */
.roster-hero-rows--stack {
  grid-template-columns: 1fr;
}

/* ---- one person, one line, everywhere on the page: bold name, dash, role
   (plus note folded in as a trailing clause) — this is the "clear character
   row" the whole page is built around ---- */
.roster-row {
  margin: 0 0 calc(0.35 * var(--base-spacing));
  font-size: 0.93429rem;
  line-height: 1.5;
}

.roster-row:last-child {
  margin-bottom: 0;
}

.roster-row strong {
  font-weight: 700;
  color: var(--lc-accent);
}

/* a linked name (not every character has a profile to link to) keeps the
   same accent color as a plain one — only the underline says it's clickable,
   so the page doesn't read as "some names are colored differently" */
.roster-row strong a,
.roster-hero-lede strong a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--lc-accent) 45%, transparent);
  text-underline-offset: 2px;
}

.roster-row strong a:hover,
.roster-hero-lede strong a:hover {
  text-decoration-color: currentColor;
}

/* ---- character status flags — icon only, right next to the name, raised
   like a footnote marker; the label for each lives once in .roster-legend
   at the top of the page instead of repeating text on every badge. Two
   independent flags (wanted/dead) share this same shape and only differ
   in color, so a second status reads as "another one of these", not a
   new kind of thing on the page ---- */
.roster-status {
  margin-left: calc(0.3 * var(--base-spacing));
  font-size: 0.7em;
  vertical-align: super;
  cursor: help;
}

.roster-status+.roster-status {
  margin-left: calc(0.15 * var(--base-spacing));
}

.roster-status--wanted {
  color: var(--lc-accent);
}

/* muted rather than the page's accent — this marks an ending, not a call
   to action the way "wanted" does, so it deliberately doesn't compete for
   the same attention */
.roster-status--dead {
  color: var(--color-neutral-muted);
}

/* ---- lands inside a territory: a card grid instead of a tab switcher —
   every land is visible at once (a directory this size is meant to be
   scanned, not clicked through one land at a time) ---- */
.roster-lands {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: calc(1.25 * var(--base-spacing));
}

.roster-land-card {
  border: 1px solid var(--lc-paper-deep);
  border-radius: 4px;
  padding: calc(1 * var(--base-spacing)) calc(1.25 * var(--base-spacing));
}

.roster-land-card-header {
  margin: 0 0 calc(0.75 * var(--base-spacing));
  padding-bottom: calc(0.5 * var(--base-spacing));
  border-bottom: 1px solid var(--lc-paper-deep);
  font-family: var(--font-heading);
  font-size: 0.85714rem;
  font-weight: 600;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  text-align: center;
  color: var(--lc-accent);
}

/* generic muted note line — a land's blurb, the ruling family's "why this
   isn't a regular land grant" aside, anywhere else a short aside fits */
.roster-note {
  font-size: 0.85714rem;
  font-style: italic;
  opacity: 0.8;
  margin: 0 0 calc(0.75 * var(--base-spacing));
}

.roster-land-ruler {
  margin: 0 0 calc(0.9 * var(--base-spacing));
  padding-bottom: calc(0.75 * var(--base-spacing));
  border-bottom: 1px dashed var(--lc-paper-deep);
}

.roster-house {
  margin: 0 0 calc(0.9 * var(--base-spacing));
}

.roster-house:last-child {
  margin-bottom: 0;
}

.roster-house-label {
  margin: 0 0 calc(0.3 * var(--base-spacing));
  font-family: var(--font-heading);
  font-size: 0.75rem;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  opacity: 0.65;
}

/* ---- a land divided into smaller territories (a county, a quarter) —
   one level in from the land card itself, so a district's own houses read
   as nested under it rather than as more houses of the land at large.
   Not every land needs this: one without districts just lists its houses
   straight under the card, as before. ---- */
.roster-district {
  margin: 0 0 calc(1 * var(--base-spacing));
  padding-left: calc(1 * var(--base-spacing));
  border-left: 2px solid var(--lc-accent);
}

.roster-district:last-child {
  margin-bottom: 0;
}

.roster-district-header {
  margin: 0 0 calc(0.4 * var(--base-spacing));
  font-family: var(--font-heading);
  font-size: 0.92857rem;
  font-weight: 700;
  color: var(--lc-accent);
}

@media (max-width: 640px) {
  .roster-territory-summary {
    padding: calc(0.75 * var(--base-spacing)) calc(1.25 * var(--base-spacing));
  }

  .roster-territory-body {
    padding: 0 calc(1.25 * var(--base-spacing)) calc(1.25 * var(--base-spacing));
  }

  .roster-hero-rows {
    grid-template-columns: 1fr;
  }

  .roster-hero-heads--split {
    grid-template-columns: 1fr;
    gap: calc(1.25 * var(--base-spacing));
  }

  .roster-hero-heads--split .roster-hero-head:first-child {
    padding-right: 0;
  }

  .roster-hero-heads--split .roster-hero-head:last-child {
    padding-left: 0;
    border-left: none;
    padding-top: calc(1.25 * var(--base-spacing));
    border-top: 1px solid var(--lc-paper-deep);
  }
}

/* ============ CHARACTER CARDS (articles/characters-*.html) ============
   Per-territory character posts: a bare .char-grid inside [html], no
   .lore-article shell, so the grid carries the base type itself and takes
   the territory color as --lc-accent inline (same convention as the lore
   articles). Classes on <p> are prefixed with .char-grid so they out-rank
   any generic `p` margin in the post iframe. The name is a <p>, not an
   <h3>: character posts are [hideprofile], and the forum's
   `.topic .post.hideprofile h3` post-header rule would out-rank it.

   Strictly two cards per row (one on phones). Each card is a subgrid over
   6 shared rows (art, office, name, latin name, bio, faceclaim), so a
   wrapping office line or long name on one card moves its neighbour's rows
   too: names, rules and bios start on the same line across a row, and the
   faceclaim footer pins both cards to the same bottom edge regardless of
   bio length. */
.char-grid {
  /* lets .char-office size itself against the row width (cqi). Set here,
     not on .char-card: container-type implies layout containment, and a
     contained element can't be a subgrid */
  container-type: inline-size;
  display: grid;
  font-family: var(--font-body);
  line-height: 1.55;
  color: var(--lc-ink);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: calc(4 * var(--base-spacing)) calc(3 * var(--base-spacing));
}

.char-card {
  --char-pad: calc(3.5 * var(--base-spacing));
  display: grid;
  grid-row: span 6;
  grid-template-rows: subgrid;
  row-gap: calc(0.5 * var(--base-spacing));
  padding: 0 var(--char-pad) calc(2.5 * var(--base-spacing));
  background: rgba(var(--color-border-rgb), 0.1);
  border: 1px solid rgba(var(--color-border-rgb), 0.3);
}

.char-portrait {
  position: relative;
  /* bleeds past the card's side padding so the art runs edge to edge */
  margin: 0 calc(-1 * var(--char-pad)) calc(1.5 * var(--base-spacing));
}

/* fixed ratio + cover: any art crops to the same banner strip */
.char-portrait img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 1;
  object-fit: cover;
  border-bottom: 3px solid var(--lc-sub-1);
}

/* role status: a ribbon wrapped round the card's left edge, swallowtail end
   pointing into the art. One word per state, told apart by fill: free is
   the lore jade, taken is the site's banner red, NPC is gilded and its
   asterisk points to the .char-note under the grid */
.char-status {
  position: absolute;
  top: calc(2 * var(--base-spacing));
  left: 0;
  padding: calc(0.5 * var(--base-spacing)) calc(2.5 * var(--base-spacing)) calc(0.5 * var(--base-spacing)) calc(1.5 * var(--base-spacing));
  clip-path: polygon(0 0, 100% 0, calc(100% - 1.25 * var(--base-spacing)) 50%, 100% 100%, 0 100%);
  font: 600 0.8rem/1.3 var(--font-body);
  color: var(--color-text-on-dark);
}

.char-status--free {
  background: var(--lc-sub-2);
}

.char-status--taken {
  background: linear-gradient(180deg, var(--color-banner-bg-light), var(--color-banner-bg));
}

.char-status--npc {
  background: var(--lc-sub-1);
  color: var(--color-heading);
}

.char-grid .char-office,
.char-grid .char-name,
.char-grid .char-latin {
  margin: 0;
  text-align: center;
  text-wrap: balance;
}

/* always one line in the two-column layout: long offices shrink with the
   row (1.4cqi of the grid ~ 3% of one card's text width) instead of
   wrapping, down to an ellipsis as a last resort; short ones stay at full
   size */
.char-grid .char-office {
  font-size: min(1rem, 1.4cqi);
  font-style: italic;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--lc-accent);
}

.char-grid .char-name {
  font: 600 2.1rem/1.05 var(--font-display);
}

.char-grid .char-latin {
  font: 500 0.72rem/1 var(--font-heading);
  letter-spacing: 0.3em;
  color: var(--color-link);
}

.char-grid .char-latin::after {
  content: "";
  display: block;
  width: 45%;
  height: 1px;
  margin: calc(1.75 * var(--base-spacing)) auto calc(0.5 * var(--base-spacing));
  background: linear-gradient(90deg, transparent, var(--lc-sub-1), transparent);
}

.char-bio {
  font-size: 0.92rem;
  line-height: 1.6;
}

.char-bio p:not(:last-child) {
  margin-bottom: calc(1.75 * var(--base-spacing));
}

.char-grid .char-faceclaim {
  align-self: end;
  margin: calc(1 * var(--base-spacing)) 0 0;
  padding-top: calc(1 * var(--base-spacing));
  border-top: 1px solid var(--lc-paper-deep);
  font-size: 0.82rem;
  opacity: 0.75;
}

/* type comes from .roster-note (same muted aside); only the spacing under
   the grid is card-specific. Flat selector: the note sits after the grid,
   not inside it */
.char-note {
  margin: calc(2 * var(--base-spacing)) 0 0;
}

@media (max-width: 720px) {
  .char-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .char-card {
    --char-pad: calc(2.5 * var(--base-spacing));
    padding-bottom: calc(2 * var(--base-spacing));
  }

  /* on a phone a 3:1 strip is too thin to read the face */
  .char-portrait img {
    aspect-ratio: 2 / 1;
  }

  .char-grid .char-name {
    font-size: 1.85rem;
  }

  /* single column: nothing to line up with, so let long offices wrap at
     full size instead of shrinking to phone-width type */
  .char-grid .char-office {
    font-size: 1rem;
    white-space: normal;
  }
}

/* ============ GUESTBOOK PAGE (articles/guestbook.html) ============
   The guestbook reads as the Empire's chancery register, not a lore
   article: one bordered sheet of parchment, centered, in the order of a
   classic forum guestbook, squeezed short — the banner carries the world
   intro, the hold terms and the GMs; a dark band of links under it; then
   the register itself and the hold form.
   The banner is the masthead, not a picture above it: the fresco bleeds to
   the sheet's edges and the title sits on its darkened foot, the way
   «IMPERIVM» sits on the index hero. It's cropped tighter than the forum
   header shows the same art (face and crack, not the whole figure), so the
   two don't read as a repeat one above the other.
   The register is the page's other loud element: two ledger columns under
   the index's crimson category band (faceclaims; roles with positions,
   grouped under a sticky header per land), filtered by land.
   The sheet is dark, the forum's raised night surface, so the post stands
   out from the parchment ones around it; the two ledger columns sit a
   step lighter, framed in gold and lifted on a shadow, so
   the register is what the eye lands on. The post renders in its own iframe (body.post-content,
   transparent), so the sheet paints its own background. Cascade-safe to append: brand-new
   .gb-* selectors, nothing earlier targets them. */
.gb-page {
  /* the banner bleeds out by exactly this padding (retuned on mobile) */
  --gb-pad-y: calc(2 * var(--base-spacing));
  --gb-pad-x: calc(4 * var(--base-spacing));
  display: flex;
  flex-direction: column;
  gap: calc(1.75 * var(--base-spacing));
  padding: var(--gb-pad-y) var(--gb-pad-x);
  background: var(--color-surface-raised);
  border: 1px solid rgba(var(--color-accent-rgb), 0.6);
  color: var(--color-text-on-dark-muted);
  font-family: var(--font-body);
  line-height: 1.6;
}

/* margins come from each container's gap, so no paragraph carries its own;
   out-ranks the frame body's .post-content p by coming later */
.gb-page p {
  margin: 0;
}

/* ---- masthead: the fresco carrying everything but the register, then the
   dark band of links under it. Bleeds out by exactly the sheet's padding ---- */
.gb-masthead {
  margin: calc(-1 * var(--gb-pad-y)) calc(-1 * var(--gb-pad-x)) 0;
}

/* the top padding is the clear sky the face shows through; everything else
   sits on the darkened foot below it */
.gb-banner {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(1 * var(--base-spacing));
  padding: calc(5 * var(--base-spacing)) var(--gb-pad-x) calc(2 * var(--base-spacing));
  background-color: var(--color-banner-bg-dark);
  background-size: 135% auto;
  background-position: 50% 14%;
  text-align: center;
  /* inherited by every line of type on the banner */
  text-shadow: var(--text-shadow-hero);
}

/* the index hero's dark foot, run higher than the hero's: the hold terms
   and the GMs need to read on any art */
.gb-banner::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(0deg, rgba(var(--color-bg-rgb), 0.96) 0%, rgba(var(--color-bg-rgb), 0.9) 55%, rgba(var(--color-bg-rgb), 0.25) 85%, rgba(var(--color-bg-rgb), 0.3) 100%);
}

.gb-banner-motto,
.gb-title,
.gb-banner-intro,
.gb-banner-foot {
  position: relative;
}

.gb-banner-motto {
  font-family: var(--font-heading);
  font-size: 0.85714rem;
  font-weight: 600;
  letter-spacing: 0.45em;
  text-transform: uppercase;
  color: var(--color-accent-light);
}

.gb-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 3.14286rem;
  font-weight: 600;
  line-height: 1;
  color: var(--color-text-on-dark);
}

/* the world in three sentences under the title, across the banner's full
   inner width, in the lighter cream of the index hero's own text */
.gb-banner-intro {
  align-self: stretch;
  font-size: 1rem;
  font-style: italic;
  text-wrap: pretty;
  color: var(--color-text-on-dark-muted);
}

/* the fine print under a gold rule: hold terms and the GMs side by side,
   a column each, in small type so the fresco stays the loud element */
.gb-banner-foot {
  align-self: stretch;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin-top: calc(1 * var(--base-spacing));
  padding-top: calc(1.5 * var(--base-spacing));
  border-top: 1px solid rgba(var(--color-accent-rgb), 0.45);
  font-size: 0.85714rem;
  line-height: 1.55;
  text-align: left;
  color: var(--color-text-on-dark-muted);
  /* the hero's wide glow smudges type this small; a tight shadow keeps it
     crisp on the already-dark foot */
  text-shadow: 0 1px 2px rgba(var(--color-shadow-rgb), 0.9);
}

.gb-banner-foot strong {
  color: var(--color-accent-light);
}

.gb-banner-note {
  display: grid;
  align-content: start;
  gap: calc(0.25 * var(--base-spacing));
  padding: 0 calc(3 * var(--base-spacing));
}

.gb-banner-note + .gb-banner-note {
  border-left: 1px solid rgba(var(--color-accent-rgb), 0.35);
}

/* the GM's name is their profile link */
.gb-gm {
  font-family: var(--font-display);
  font-size: 1rem;
  font-style: italic;
  font-weight: 600;
  color: var(--color-text-on-dark);
  text-decoration: none;
}

/* ---- the hold form's box: a shade lighter than the sheet ---- */
.gb-box {
  display: grid;
  gap: calc(1 * var(--base-spacing));
  padding: calc(1.5 * var(--base-spacing)) calc(3 * var(--base-spacing));
  background: var(--color-banner-bg-dark);
  border: 1px solid rgba(var(--color-accent-rgb), 0.35);
}

/* ---- useful links: a dark band right under the fresco, set as the forum
   header's news strip — the same pills (hover shared with .news-item, see
   NEWS STRIP), the icon in the date's gold coin. One row: centered while it
   fits, swiped sideways on a phone ---- */
.gb-nav {
  padding: calc(1.25 * var(--base-spacing)) 0;
  background: var(--color-banner-bg-dark);
  border-top: 1px solid var(--color-accent);
  border-bottom: 1px solid rgba(var(--color-accent-rgb), 0.6);
}

.gb-links {
  display: flex;
  justify-content: safe center;
  gap: calc(1.5 * var(--base-spacing));
  margin: 0;
  /* the side padding lives inside the scroller, so a swiped row runs out
     to the band's edges instead of being clipped short of them */
  padding: 0 var(--gb-pad-x);
  overflow-x: auto;
  list-style: none;
  scrollbar-width: none;
}

.gb-links li {
  flex: none;
}

.gb-link {
  display: flex;
  align-items: center;
  gap: calc(1.25 * var(--base-spacing));
  padding: calc(0.625 * var(--base-spacing)) calc(2.5 * var(--base-spacing)) calc(0.625 * var(--base-spacing)) calc(0.625 * var(--base-spacing));
  border: 1px solid rgba(var(--color-accent-rgb), 0.25);
  border-radius: calc(4 * var(--base-spacing));
  font-size: 0.92857rem;
  font-weight: 600;
  line-height: 1.25;
  color: var(--color-text-on-dark);
  text-decoration: none;
  transition: border-color 0.2s, background-color 0.2s;
}

/* .news-date's coin; the gap ring is the band's own color */
.gb-link-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: calc(4 * var(--base-spacing));
  height: calc(4 * var(--base-spacing));
  border: 1px solid var(--color-accent);
  border-radius: 50%;
  box-shadow: inset 0 0 0 2px var(--color-banner-bg-dark), inset 0 0 0 3px rgba(var(--color-accent-rgb), 0.35);
  font-size: 0.78571rem;
  color: var(--color-accent-light);
}

.gb-gm:hover {
  color: var(--color-accent-light);
}

/* one focus ring for every link and button on the sheet */
.gb-link:focus-visible,
.gb-gm:focus-visible,
.gb-land:focus-visible {
  outline: 2px solid var(--color-accent-light);
  outline-offset: 2px;
}

/* ---- sections with a centered title between two gold rules (the index's
   category header, on parchment instead of crimson) ---- */
/* minmax(0, 1fr): an implicit auto column would size to its widest child's
   max-content (the ledger's two columns) and overflow the sheet instead
   of letting the child's own grid wrap */
.gb-section {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: calc(1.5 * var(--base-spacing));
}

/* the tracked display caps every kind of title shares */
.gb-heading,
.gb-col-title,
.gb-note-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

.gb-heading {
  display: flex;
  align-items: center;
  gap: calc(2 * var(--base-spacing));
  font-size: 1.21429rem;
  color: var(--color-accent-light);
}

.gb-heading::before,
.gb-heading::after {
  content: "";
  flex: 1;
  height: 1px;
  background: rgba(var(--color-accent-rgb), 0.6);
}

/* the small gold label over each column of the banner's fine print */
.gb-note-title {
  font-size: 0.85714rem;
  color: var(--color-accent-light);
}

/* ---- land filter ---- */
.gb-lands {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: calc(1 * var(--base-spacing));
}

.gb-land {
  display: inline-flex;
  align-items: center;
  gap: calc(0.75 * var(--base-spacing));
  padding: calc(0.625 * var(--base-spacing)) calc(1.75 * var(--base-spacing));
  background: transparent;
  border: 1px solid rgba(var(--color-accent-rgb), 0.4);
  border-radius: calc(4 * var(--base-spacing));
  font-family: var(--font-body);
  font-size: 0.92857rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--color-text-on-dark-subtle);
  cursor: pointer;
}

.gb-land:hover {
  border-color: var(--color-accent);
}

/* the chosen land wears the crimson band, like the active tab elsewhere */
.gb-land[aria-pressed="true"] {
  background: var(--gradient-banner);
  border-color: var(--color-accent);
  color: var(--color-text-on-dark);
}

.gb-land-count {
  font-size: 0.78571rem;
  font-variant-numeric: tabular-nums;
  opacity: 0.7;
}

/* ---- ledger ---- */
.gb-ledger {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: calc(2 * var(--base-spacing));
}

/* a step up from the sheet, framed in gold and lifted on a shadow — the
   register is the one raised thing on the page */
.gb-col {
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--color-banner-bg-dark);
  border: 1px solid var(--color-accent);
  box-shadow: 0 0 0 calc(0.5 * var(--base-spacing)) rgba(var(--color-accent-rgb), 0.08), 0 calc(1 * var(--base-spacing)) calc(3 * var(--base-spacing)) rgba(var(--color-shadow-rgb), 0.6);
  color: var(--color-text-on-dark);
}

.gb-col-title {
  padding: calc(0.75 * var(--base-spacing)) calc(2 * var(--base-spacing));
  background: var(--gradient-banner);
  border-bottom: 1px solid var(--color-accent);
  font-size: 1.07143rem;
  text-align: center;
  color: var(--color-text-on-dark);
}

/* fixed height: both columns stay the same scannable strip however long a
   list grows, and switching lands never changes the post's height */
.gb-list {
  height: calc(26 * var(--base-spacing));
  margin: 0;
  padding: 0 calc(2 * var(--base-spacing)) calc(0.5 * var(--base-spacing));
  overflow-y: auto;
  list-style: none;
  scrollbar-width: thin;
  scrollbar-color: rgba(var(--color-accent-rgb), 0.6) transparent;
}

/* land header inside the roles list: a tinted band with the land's role
   count on the right. Sticks to the top of the scrolling column while its roles pass
   under it, and bleeds over the list's side padding so it spans the
   column. Opaque background — roles scroll beneath it */
.gb-group-title {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: calc(1.5 * var(--base-spacing));
  margin: 0 calc(-2 * var(--base-spacing));
  padding: calc(1 * var(--base-spacing)) calc(2 * var(--base-spacing));
  background: color-mix(in srgb, var(--color-accent) 10%, var(--color-banner-bg-dark));
  border-bottom: 1px solid rgba(var(--color-accent-rgb), 0.6);
  font-family: var(--font-display);
  font-size: 1.14286rem;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--color-accent-light);
}

/* a gap and a gold rule before every land after the first; the first sits
   right under the column title, whose own gold rule already closes it */
.gb-group-title:not(:first-child) {
  margin-top: calc(2 * var(--base-spacing));
  border-top: 1px solid rgba(var(--color-accent-rgb), 0.6);
}

.gb-group-count {
  font-family: var(--font-body);
  font-size: 0.78571rem;
  font-style: italic;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--color-text-on-dark-subtle);
}

/* the optional hold date, then a body holding the entry and the player's
   nickname. Flex, not a grid: a row without a date leaves no empty column
   or stray gap behind, and the nickname always ends the row on the same
   right edge. On phones only the body changes direction (see below) */
.gb-row,
.gb-row-body {
  display: flex;
  align-items: center;
  gap: calc(1.25 * var(--base-spacing));
}

.gb-row {
  padding: calc(0.75 * var(--base-spacing)) 0;
  border-bottom: 1px dashed rgba(var(--color-accent-rgb), 0.2);
  line-height: 1.3;
}

.gb-row-body {
  flex: 1 1 auto;
  min-width: 0;
}

/* no dashed rule under a land's last role — the next land's band already
   divides them */
.gb-row:last-child,
.gb-row:has(+ .gb-group-title) {
  border-bottom: none;
}

/* Latin faceclaim names set like inscriptions; Cinzel has no Cyrillic, so
   it's only ever used on them */
.gb-face {
  font-family: var(--font-heading);
  font-size: 0.92857rem;
  font-weight: 600;
  letter-spacing: 0.06em;
}

/* long names and nicknames wrap — and break inside a word with no spaces —
   instead of pushing past the column */
.gb-entry {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: calc(0.25 * var(--base-spacing));
  min-width: 0;
  overflow-wrap: anywhere;
}

.gb-kind {
  font-size: 0.85714rem;
  font-style: italic;
  color: var(--color-text-on-dark-subtle);
}

.gb-name {
  font-family: var(--font-display);
  font-size: 1.21429rem;
  font-weight: 600;
}

/* one line, capped at half the row: a long nickname ends in an ellipsis
   (full text in its title) instead of squeezing the entry or breaking
   mid-word */
.gb-player {
  flex: 0 1 auto;
  max-width: 50%;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: 0.85714rem;
  color: var(--color-text-on-dark-subtle);
  text-decoration: none;
}

.gb-player[href]:hover {
  color: var(--color-accent-light);
  text-decoration: underline;
}

.gb-held-date {
  flex: none;
  padding: calc(0.25 * var(--base-spacing)) calc(0.75 * var(--base-spacing));
  background: rgba(var(--color-accent-rgb), 0.15);
  border: 1px solid rgba(var(--color-accent-rgb), 0.5);
  border-radius: calc(0.5 * var(--base-spacing));
  font-size: 0.78571rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--color-accent-light);
}

.gb-empty {
  padding: calc(1.5 * var(--base-spacing)) 0;
  font-size: 0.92857rem;
  font-style: italic;
  text-align: center;
  color: var(--color-neutral-muted);
}

/* ---- hold form: inputs and the button are the forum's own REUSABLE FORM
   FIELDS / REUSABLE BUTTON, only the layout is set here ---- */
.gb-hold {
  grid-template-columns: repeat(4, minmax(0, 1fr)) auto;
  align-items: end;
}

.gb-field {
  display: grid;
  gap: calc(0.5 * var(--base-spacing));
  font-size: 0.78571rem;
  font-weight: 600;
  color: var(--color-text-on-dark-subtle);
}

.gb-input {
  width: 100%;
}

.gb-submit {
  white-space: nowrap;
}

/* starts with the hint, then says whether the copy worked */
.gb-hold-status {
  grid-column: 1 / -1;
  min-height: 1.6em;
  font-size: 0.85714rem;
  font-style: italic;
  color: var(--color-text-on-dark-subtle);
}

@media (prefers-reduced-motion: reduce) {
  .gb-link {
    transition: none;
  }
}

@media (max-width: 720px) {
  .gb-page {
    --gb-pad-y: calc(2 * var(--base-spacing));
    --gb-pad-x: calc(2 * var(--base-spacing));
  }

  /* cover, not 135%: at phone width the text runs far down the banner and
     a width-sized fresco would end above it */
  .gb-banner {
    padding-top: calc(4 * var(--base-spacing));
    background-size: cover;
    background-position: 50% 10%;
  }

  /* the fine print stacks: terms over GMs, a gold rule between them */
  .gb-banner-foot {
    grid-template-columns: minmax(0, 1fr);
    gap: calc(1.5 * var(--base-spacing));
  }

  .gb-banner-note {
    padding: 0;
  }

  .gb-banner-note + .gb-banner-note {
    padding-top: calc(1.5 * var(--base-spacing));
    border-top: 1px solid rgba(var(--color-accent-rgb), 0.35);
    border-left: none;
  }

  .gb-banner-motto {
    letter-spacing: 0.3em;
  }

  .gb-title {
    font-size: 2.57143rem;
  }

  .gb-box {
    padding: calc(1.5 * var(--base-spacing)) calc(2 * var(--base-spacing));
  }

  .gb-heading {
    font-size: 1.07143rem;
    letter-spacing: 0.12em;
  }

  .gb-ledger,
  .gb-hold {
    grid-template-columns: minmax(0, 1fr);
  }

  /* one swipeable row of lands instead of three ragged wrapped rows — the
     index's news strip does the same on phones. Bleeds to the sheet's
     edges so the row scrolls under them */
  .gb-lands {
    flex-wrap: nowrap;
    justify-content: flex-start;
    margin: 0 calc(-1 * var(--gb-pad-x));
    padding: 0 var(--gb-pad-x) calc(0.5 * var(--base-spacing));
    overflow-x: auto;
    scroll-snap-type: x proximity;
    /* snap stops at the padding, not the strip's bare edge */
    scroll-padding-inline: var(--gb-pad-x);
    scrollbar-width: none;
  }

  .gb-land {
    flex: none;
    scroll-snap-align: start;
  }

  /* no box scrolling inside the page on a touchscreen: the lists open to
     their full length and scroll with the page (the post's frame grows to
     fit) */
  .gb-list {
    height: auto;
    overflow-y: visible;
  }

  /* a phone row has no room for name and nickname side by side once
     either runs long, so the body stacks them — entry over nickname, the
     date (if any) still centered beside both */
  .gb-row-body {
    flex-direction: column;
    align-items: stretch;
    gap: calc(0.5 * var(--base-spacing));
  }

  .gb-player {
    max-width: none;
  }

  /* marks the line as the player, not more about the character */
  .gb-player::before {
    content: "\f007";
    /* fa-user */
    margin-right: calc(0.5 * var(--base-spacing));
    font: normal 900 0.64286rem "Font Awesome 6 Free";
    color: var(--color-accent);
  }
}

/* ---- TERRITORY HEADER (.lore-terr) ----
   The world overview's one dark block: title, tagline and a short intro
   beside the political map, and the guestbook's link band (.gb-nav, style/style.css) as its foot.
   Cascade-safe: brand-new selectors, placed last (after the LORE ARTICLE TEMPLATE
   in colors.css, which style.css inlines at its top). */
.lore-article .lore-terr {
  /* .gb-links reads this for its side padding; same inset as the article */
  --gb-pad-x: var(--lc-pad-x);
  --lc-hero-ink: var(--color-text-on-dark);
  --lc-hero-gold: var(--color-accent-light);
  --lc-hero-shadow: var(--text-shadow-hero);
  /* the block is the whole article, so it also covers the bottom padding */
  margin: calc(-1 * var(--lc-pad-y)) calc(-1 * var(--lc-pad-x));
  background: linear-gradient(180deg, var(--color-banner-bg-dark), var(--color-bg));
  text-align: left;
}

.lore-article .lore-terr-main {
  display: grid;
  grid-template-columns: minmax(0, 1fr) calc(48 * var(--base-spacing));
  gap: calc(3 * var(--base-spacing));
  align-items: center;
  padding: var(--lc-pad-y) var(--lc-pad-x);
}

.lore-article .lore-terr h1 {
  margin: 0 0 calc(1.25 * var(--base-spacing));
  font-size: 2.57143rem;
}

.lore-article .lore-terr .lore-tagline {
  margin: 0 0 calc(2 * var(--base-spacing));
}

.lore-article .lore-terr-intro {
  margin: 0;
  font-size: 1.14286rem;
  color: var(--color-text-on-dark);
}

.lore-article .lore-terr-intro a {
  color: var(--color-accent-light);
}

.lore-article .lore-terr-map {
  display: block;
  aspect-ratio: 4 / 3;
  border: 1px solid rgba(var(--color-accent-rgb), 0.6);
  cursor: zoom-in;
}

.lore-article .lore-terr-map img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.lore-article .lore-terr-map:focus-visible {
  outline: 2px solid var(--color-accent-light);
  outline-offset: 2px;
}

/* no map (organizations overview): the text takes the whole row */
.lore-article .lore-terr--plain .lore-terr-main {
  grid-template-columns: minmax(0, 1fr);
}

/* a rule before the first link of a second group in the link band: the
   guide's forum links come first, the canon (magic, the Break) after it */
.lore-article .lore-terr-split {
  padding-left: calc(1.5 * var(--base-spacing));
  border-left: 1px solid rgba(var(--color-accent-rgb), 0.6);
}

/* the guide's intro runs several paragraphs */
.lore-article .lore-terr-intro + .lore-terr-intro {
  margin-top: calc(1.5 * var(--base-spacing));
}

/* ---- GUIDE (articles/guide.html) ----
   One card per land or organization: a dark plate (the .lore-terr head's own
   gradient) with the emblem and name beside the blurb, and for lands the facts
   as a dark ledger under both, like the facts strip of the country articles.
   The plate is exactly one ledger cell wide (a fifth of the card), so the
   plate's edge continues down as the first fact's.
   The .lore-terr head's margin pulls the next block up by --lc-pad-y (it
   covers the article's bottom padding), so the guide pushes itself back down
   by the same amount and its first band sits flush under the head; the
   bottom margin likewise runs the last card to the article's lower edge.
   Banners and cards stack with no gaps, split by gold hairlines. */
.lore-article .lore-guide {
  margin: var(--lc-pad-y) 0 calc(-1 * var(--lc-pad-y));
}

/* each group opens with the same full-bleed band, like the .lore-terr head */
.lore-article .lore-guide-banner {
  margin: 0 calc(-1 * var(--lc-pad-x));
  padding: calc(1.5 * var(--base-spacing)) var(--lc-pad-x);
  background: linear-gradient(180deg, var(--color-banner-bg-light), var(--color-banner-bg));
  border: 1px solid rgba(var(--color-accent-rgb), 0.6);
  border-top: 3px solid var(--color-accent);
  text-align: center;
}

/* the gold-ruled centered title of the guestbook's section headings */
.lore-article .lore-guide-banner h2 {
  display: flex;
  align-items: center;
  gap: calc(2 * var(--base-spacing));
  margin: 0 0 calc(0.25 * var(--base-spacing));
  font-size: 1.42857rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--color-accent-light);
}

.lore-article .lore-guide-banner h2::before,
.lore-article .lore-guide-banner h2::after {
  content: "";
  flex: 1;
  height: 1px;
  background: rgba(var(--color-accent-rgb), 0.6);
}

.lore-article .lore-guide-banner p {
  margin: 0;
  font-size: 0.85714rem;
  font-style: italic;
  color: var(--color-text-on-dark-subtle);
}

/* cards run edge to edge of the article, no side frame */
.lore-article .lore-guide-list {
  margin: 0 calc(-1 * var(--lc-pad-x));
}

.lore-article .lore-guide-card {
  display: grid;
  grid-template-columns: calc(100% / 5) minmax(0, 1fr);
  background: var(--color-panel-bg-alt);
  border: solid rgba(var(--color-accent-rgb), 0.6);
  border-width: 0 1px;
}

/* the heavy gold rule is the explicit division: above every card but the
   first of a group (its band already carries one) and under the last card */
.lore-article .lore-guide-card + .lore-guide-card {
  border-top: 3px solid var(--color-accent);
  /* the gold alone washes out against the light blurb panel on either side of
     it: a soft shadow lifts it above the previous card and below it */
  box-shadow:
    0 calc(-0.25 * var(--base-spacing)) calc(1 * var(--base-spacing)) calc(-0.75 * var(--base-spacing)) rgba(var(--color-shadow-rgb), 0.35),
    inset 0 calc(1 * var(--base-spacing)) calc(1 * var(--base-spacing)) calc(-0.5 * var(--base-spacing)) rgba(var(--color-shadow-rgb), 0.3);
}

/* the organizations have no dark ledger above the rule, so it sits between two
   light panels: a fainter shadow, or it reads as a smudge */
.lore-article .lore-guide-group + .lore-guide-group .lore-guide-card + .lore-guide-card {
  box-shadow:
    0 calc(-0.25 * var(--base-spacing)) calc(1 * var(--base-spacing)) calc(-0.75 * var(--base-spacing)) rgba(var(--color-shadow-rgb), 0.07),
    inset 0 calc(1 * var(--base-spacing)) calc(1 * var(--base-spacing)) calc(-0.5 * var(--base-spacing)) rgba(var(--color-shadow-rgb), 0.06);
}

.lore-article .lore-guide-group:last-child .lore-guide-card:last-child {
  border-bottom: 3px solid var(--color-accent);
}

.lore-article .lore-guide-plate {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(1 * var(--base-spacing));
  /* tall enough for a two-line name, so every card is one height even
     without a ledger (the organizations) */
  min-height: calc(33 * var(--base-spacing));
  padding: calc(3 * var(--base-spacing)) calc(1 * var(--base-spacing));
  background: linear-gradient(180deg, var(--color-banner-bg-dark), var(--color-bg));
  text-align: center;
}

/* the link band's coin (.gb-link-icon), grown to a seal */
.lore-article .lore-guide-emblem {
  display: grid;
  place-items: center;
  width: calc(9 * var(--base-spacing));
  height: calc(9 * var(--base-spacing));
  margin-bottom: calc(1 * var(--base-spacing));
  border: 1px solid var(--color-accent);
  border-radius: 50%;
  box-shadow: inset 0 0 0 3px var(--color-banner-bg-dark), inset 0 0 0 4px rgba(var(--color-accent-rgb), 0.35);
  font-size: 2rem;
  color: var(--color-accent-light);
}

.lore-article .lore-guide-plate h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.85714rem;
  font-weight: 600;
  line-height: 1.1;
  color: var(--color-accent-bright);
}

.lore-article .lore-guide-badge {
  margin: 0;
  font-size: 0.85714rem;
  font-style: italic;
  color: var(--color-text-on-dark-subtle);
}

.lore-article .lore-guide-body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: calc(2 * var(--base-spacing));
  padding: calc(3 * var(--base-spacing));
}

.lore-article .lore-guide-body p {
  margin: 0;
  max-width: calc(90 * var(--base-spacing));
  font-size: 1.07143rem;
  line-height: 1.6;
  text-align: left;
}

.lore-article .lore-guide-facts {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  margin: 0;
  background: rgba(var(--color-bg-rgb), 0.92);
  border-top: 1px solid rgba(var(--color-accent-rgb), 0.6);
}

.lore-article .lore-guide-facts > div {
  padding: calc(2 * var(--base-spacing));
}

.lore-article .lore-guide-facts > div + div {
  border-left: 1px solid rgba(var(--color-accent-rgb), 0.2);
}

.lore-article .lore-guide-facts dt {
  margin: 0 0 calc(0.25 * var(--base-spacing));
  font-size: 0.78571rem;
  font-weight: 600;
  color: var(--color-accent-light);
}

.lore-article .lore-guide-facts dd {
  margin: 0;
  font-size: 0.89286rem;
  line-height: 1.4;
  text-align: left;
  color: var(--color-text-on-dark);
}

/* the links sit in the plate, under the name */
.lore-article .lore-guide-links {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  justify-content: center;
  gap: calc(0.75 * var(--base-spacing));
  margin-top: calc(1 * var(--base-spacing));
}

.lore-article .lore-guide-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: calc(0.75 * var(--base-spacing));
  padding: calc(0.5 * var(--base-spacing)) calc(0.75 * var(--base-spacing));
  border: 1px solid rgba(var(--color-accent-rgb), 0.6);
  border-radius: calc(4 * var(--base-spacing));
  font-size: 0.75rem;
  white-space: nowrap;
  font-weight: 600;
  text-decoration: none;
  color: var(--color-accent-light);
  transition: background-color 0.2s;
}

.lore-article .lore-guide-link:hover {
  background: rgba(var(--color-accent-rgb), 0.16);
}

.lore-article .lore-guide-link:focus-visible {
  outline: 2px solid var(--lc-accent);
  outline-offset: 2px;
}

@media (max-width: 720px) {
  .lore-article .lore-terr-main {
    grid-template-columns: minmax(0, 1fr);
    gap: calc(2 * var(--base-spacing));
  }

  .lore-article .lore-terr h1 {
    font-size: 2.14286rem;
  }

  .lore-article .lore-terr-map {
    order: -1;
  }

  .lore-article .lore-guide-banner h2 {
    gap: calc(1.5 * var(--base-spacing));
    font-size: 1.14286rem;
    letter-spacing: 0.12em;
  }

  /* plate becomes a banner over the blurb; ledger cells stack in two columns */
  .lore-article .lore-guide-card {
    grid-template-columns: minmax(0, 1fr);
  }

  .lore-article .lore-guide-plate {
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: flex-start;
    gap: calc(0.75 * var(--base-spacing)) calc(1.5 * var(--base-spacing));
    min-height: 0;
    text-align: left;
    padding: calc(1.5 * var(--base-spacing)) calc(2 * var(--base-spacing));
  }

  .lore-article .lore-guide-emblem {
    width: calc(6 * var(--base-spacing));
    height: calc(6 * var(--base-spacing));
    margin-bottom: 0;
    font-size: 1.28571rem;
  }

  .lore-article .lore-guide-plate h3 {
    flex: 1;
    font-size: 1.57143rem;
  }

  .lore-article .lore-guide-badge {
    flex-basis: 100%;
  }

  .lore-article .lore-guide-links {
    flex-basis: 100%;
    margin-top: 0;
  }

  .lore-article .lore-guide-body {
    padding: calc(1.5 * var(--base-spacing)) calc(2 * var(--base-spacing));
  }

  .lore-article .lore-guide-body p {
    font-size: 1rem;
    line-height: 1.5;
  }

  .lore-article .lore-guide-facts > div {
    padding: calc(1.25 * var(--base-spacing)) calc(2 * var(--base-spacing));
  }

  .lore-article .lore-guide-facts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .lore-article .lore-guide-facts > div:nth-child(odd) {
    border-left: none;
  }

  .lore-article .lore-guide-facts > div:nth-child(n+3) {
    border-top: 1px solid rgba(var(--color-accent-rgb), 0.2);
  }

  /* a fifth fact would sit alone in the last row: let it take the whole row */
  .lore-article .lore-guide-facts > div:last-child:nth-child(odd) {
    grid-column: 1 / -1;
    border-left: none;
  }
}

/* ============ WORDLE (wordle/wordle.html) ============
   The post renders in its own transparent iframe over the forum's parchment
   panel, so the sheet paints no background and all text is dark-on-parchment.
   Tiles and keys borrow the forum's palette: empty = brown hairline, green =
   jade (--lc-sub-2), yellow = muted gold (--color-link), gray = muted taupe.
   Cormorant (--font-display) for letters and titles because Cinzel has no
   Cyrillic. Cascade-safe to append: brand-new #wg / .wg-* selectors. */
#wg {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  max-width: calc(48 * var(--base-spacing));
  margin: 0 auto;
  padding: calc(2 * var(--base-spacing));
  background: transparent;
  color: var(--color-text);
  font-family: var(--font-body);
  line-height: 1.6;
  user-select: none;
  -webkit-user-select: none;
}

#wg-board {
  display: flex;
  flex-direction: column;
  gap: calc(0.625 * var(--base-spacing));
  margin-bottom: calc(1.75 * var(--base-spacing));
}

.wg-row {
  display: flex;
  gap: calc(0.625 * var(--base-spacing));
}

.wg-tile {
  display: flex;
  align-items: center;
  justify-content: center;
  width: calc(7 * var(--base-spacing));
  height: calc(7 * var(--base-spacing));
  border: 1px solid rgba(var(--color-border-rgb), 0.35);
  background: rgba(var(--color-border-rgb), 0.06);
  font-family: var(--font-display);
  font-size: 2rem;
  font-weight: 600;
  color: var(--color-text);
}

.wg-tile.wg-has-letter {
  border-color: var(--color-border);
  background: rgba(var(--color-accent-rgb), 0.12);
}

.wg-tile.wg-green,
.wg-key.wg-green {
  background: var(--lc-sub-2);
  border-color: var(--lc-sub-2);
  color: var(--color-text-on-dark);
}

.wg-tile.wg-yellow,
.wg-key.wg-yellow {
  background: var(--color-link);
  border-color: var(--color-link);
  color: var(--color-text-on-dark);
}

.wg-tile.wg-gray,
.wg-key.wg-gray {
  background: var(--color-neutral-muted);
  border-color: var(--color-neutral-muted);
  color: var(--color-panel-bg-alt);
}

@keyframes wg-pop {
  50% { transform: scale(1.1); }
}

@keyframes wg-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-7px); }
  40% { transform: translateX(7px); }
  60% { transform: translateX(-4px); }
  80% { transform: translateX(4px); }
}

#wg-msg {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: calc(3.5 * var(--base-spacing));
  margin-bottom: calc(1.75 * var(--base-spacing));
  padding: 0 calc(2 * var(--base-spacing));
  background: rgba(var(--color-border-rgb), 0.12);
  border: 1px solid rgba(var(--color-border-rgb), 0.4);
  border-radius: calc(0.5 * var(--base-spacing));
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: 1px;
  text-align: center;
  color: var(--color-heading);
  opacity: 0;
  transition: opacity 0.2s;
}

#wg-keyboard {
  display: flex;
  flex-direction: column;
  gap: calc(0.75 * var(--base-spacing));
  width: 100%;
}

.wg-kb-row {
  display: flex;
  justify-content: center;
  gap: calc(0.5 * var(--base-spacing));
}

.wg-key {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1 1 0;
  min-width: 0;
  max-width: calc(4.5 * var(--base-spacing));
  height: calc(6.25 * var(--base-spacing));
  padding: 0;
  background: var(--lc-paper-deep);
  border: 1px solid rgba(var(--color-border-rgb), 0.5);
  border-radius: calc(0.5 * var(--base-spacing));
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
  color: var(--color-heading);
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}

.wg-key:hover {
  border-color: var(--color-border);
}

.wg-key:active {
  opacity: 0.75;
}

.wg-key.wg-wide {
  flex-grow: 1.6;
  max-width: calc(7.5 * var(--base-spacing));
  font-size: 0.85714rem;
}

#wg-share,
#wg-hint {
  display: none;
  margin-top: calc(1.5 * var(--base-spacing));
}

#wg-loader {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(1.5 * var(--base-spacing));
  padding: calc(4 * var(--base-spacing)) 0;
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 3px;
  text-transform: uppercase;
  color: var(--color-link);
}

#wg-loader-spinner {
  width: calc(3.5 * var(--base-spacing));
  height: calc(3.5 * var(--base-spacing));
  border: 2px solid rgba(var(--color-border-rgb), 0.2);
  border-top-color: var(--color-border);
  border-radius: 50%;
  animation: wg-spin 0.8s linear infinite;
}

@keyframes wg-spin {
  to { transform: rotate(360deg); }
}

@media (max-width: 400px) {
  #wg {
    padding: calc(1.5 * var(--base-spacing)) calc(1 * var(--base-spacing));
  }

  .wg-tile {
    width: calc(6 * var(--base-spacing));
    height: calc(6 * var(--base-spacing));
    font-size: 1.7rem;
  }
}
