/*
 * ── THE CATALOGUE LOOK, SHARED BY BOTH SURFACES ─────────────────────────────────────────────────
 *
 * The trader's offer, the field console's game database and the admin panel's game database are one
 * design: a category rail with pictures down the left (a scrolling row on a phone), rows with a
 * picture and a family line beside it, and a record page behind every row. The markup for all of it
 * is built once, in `itemRecord.js` beside this file, and this is its one stylesheet. Both pages link
 * it AFTER their own `style.css`, so a rule here wins a tie with a surface's own.
 *
 * It used to be the field console's alone, and the admin panel had its own grid of tiles in its own
 * sheet. A copy of these rules in the panel's stylesheet would be two answers about one look, which
 * drift the first time somebody tunes a row on one of them.
 *
 * Every value here is a theme TOKEN both sheets define (`--panel-2`, `--inset`, `--line`, `--amber`,
 * `--muted`, `--faint`, `--hover-bg`, `--radius`). Every button states its own font, weight and
 * alignment, because the admin panel's `button` paints every button amber, bold and centred, and
 * every hover states BOTH halves of the pair — `check-contrast` reads this sheet with the panel's.
 * `check-mobile` reads it too.
 */

/* ── The rail and the rows ─────────────────────────────────────────────────────────────────── */
.offer-shop { display: grid; grid-template-columns: minmax(0, 15rem) minmax(0, 1fr); gap: 1rem; align-items: start; }
.offer-cats { display: flex; flex-direction: column; gap: 0.2rem; position: sticky; top: 0; }
.offer-cat {
  display: flex; align-items: center; justify-content: flex-start; gap: 0.5rem; width: 100%; min-width: 0; padding: 0.35rem 0.5rem;
  background: transparent; border: 1px solid transparent; border-radius: var(--radius);
  color: var(--muted); font-family: var(--ui); font-size: 0.85rem; font-weight: 400; letter-spacing: 0;
  text-align: left; text-transform: none; cursor: pointer;
}
.offer-cat.d1 { padding-left: 1.4rem; font-size: 0.8rem; }
.offer-cat.d2, .offer-cat.d3 { padding-left: 2.3rem; font-size: 0.78rem; }
.offer-cat:hover, .offer-cat:focus-visible { background: var(--hover-bg); color: var(--text); border-color: var(--line); outline: none; }
.offer-cat.open { color: var(--text); background: transparent; border-color: transparent; }
.offer-cat.open:hover { background: var(--hover-bg); color: var(--text); border-color: var(--line); }
.offer-cat.active, .offer-cat.active:hover { background: var(--panel-2); color: var(--amber-hi); border-color: var(--line-amber); }
.offer-cat:focus-visible { outline: 2px solid var(--amber); outline-offset: -2px; }
/* The category's picture sits OVER its family glyph. A picture that never arrives is removed by
   `imgRetry(this,'hide')` and the glyph under it is what is left, rather than an empty square. */
.offer-cat-img {
  position: relative; flex: none; display: inline-grid; place-items: center; width: 1.6rem; height: 1.6rem;
  border-radius: var(--radius); background: var(--inset); overflow: hidden; color: var(--amber);
}
.offer-cat-img img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: var(--inset); }
.offer-cat-img .ico { width: 0.9rem; height: 0.9rem; }
.offer-cat-name { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.offer-cat-n { flex: none; font-family: var(--mono); font-size: 0.75rem; color: var(--faint); }
.offer-cat.active .offer-cat-n { color: var(--amber-hi); }
.offer-list { min-width: 0; }
.offer-grp + .offer-grp { margin-top: 0.9rem; }
.offer-gh {
  display: flex; align-items: center; gap: 0.45rem; margin: 0 0 0.3rem; padding-bottom: 0.3rem;
  border-bottom: 1px solid var(--line-amber); color: var(--amber); font-size: 0.78rem; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase;
}
.offer-gh img { width: 1.3rem; height: 1.3rem; object-fit: contain; }
.offer-gn { margin-left: auto; font-family: var(--mono); color: var(--faint); letter-spacing: 0; }
/* Two columns of rows where there is room: a row is a picture, a name and a short second line, and
   one row across a 900px list is mostly empty space. */
.offer-rows { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr)); column-gap: 1.4rem; }
.offer-row { display: grid; grid-template-columns: 2.4rem minmax(0, 1fr) auto; align-items: center; gap: 0.65rem; padding: 0.4rem 0.1rem; border-bottom: 1px solid var(--line); }
.offer-img { display: inline-grid; place-items: center; width: 2.4rem; height: 2.4rem; border-radius: var(--radius); background: var(--inset); overflow: hidden; color: var(--muted); }
.offer-img img { width: 100%; height: 100%; object-fit: contain; }
.offer-img .ico { width: 1rem; height: 1rem; }
/* hyphens: auto, because a narrow row in a long language still has to break an item name somewhere,
   and at a syllable with a hyphen reads as a word; at whatever letter met the edge it does not. */
.offer-name { color: var(--text); font-size: 0.92rem; overflow-wrap: break-word; hyphens: auto; }
.offer-side { display: flex; flex-direction: column; align-items: flex-end; gap: 0.15rem; color: var(--muted); font-size: 0.78rem; white-space: nowrap; }
.offer-stock.out { color: var(--alarm); font-weight: 600; }
.offer-fame { display: inline-flex; align-items: center; gap: 0.2rem; }
.offer-fame .ico { width: 0.75rem; height: 0.75rem; color: var(--amber); }
.offer-row.out .offer-name { color: var(--muted); }
/* A row that OPENS the record — every row of an offer and of both databases. A button on the field
   console and a LINK in the admin panel (a row there is an address that can be opened in a new tab),
   so both are reset to read as the row they sit in; the hover tints the row and the chevron lights. */
button.offer-row, a.offer-row {
  width: 100%; min-width: 0; margin: 0; cursor: pointer; text-align: left; text-decoration: none;
  background: transparent; border: 0; border-bottom: 1px solid var(--line); border-radius: 0;
  color: var(--text); font-family: var(--ui); font-size: inherit; font-weight: 400; letter-spacing: 0;
  justify-content: stretch; padding: 0.4rem 0.3rem;
  --hov-bg: var(--hover-bg); --hov-fg: var(--text); --hov-line: var(--line);
}
button.offer-row:hover, button.offer-row:focus-visible,
a.offer-row:hover, a.offer-row:focus-visible { background: var(--hov-bg); color: var(--hov-fg); border-color: var(--hov-line); transform: none; outline: none; }
button.offer-row:hover .item-chev, button.offer-row:focus-visible .item-chev,
a.offer-row:hover .item-chev, a.offer-row:focus-visible .item-chev { color: var(--amber-hi); }
.offer-empty { padding: 1rem 0; }
.offer-more {
  display: block; width: 100%; margin-top: 0.7rem; padding: 0.5rem; cursor: pointer;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--text); font-family: var(--ui); font-size: 0.85rem; font-weight: 600; letter-spacing: 0;
}
.offer-more:hover, .offer-more:focus-visible { background: var(--hover-bg); color: var(--amber-hi); border-color: var(--line-amber); outline: none; }

/* The database's own half of the same look: a row is a BUTTON or a LINK and carries a second line
   (the family) and, in the admin panel, a third (the game code). */
.items-main { min-width: 0; }
/* THE RAIL SCROLLS ON ITS OWN. A rail of forty categories beside a list of hundreds made the reader
   scroll the whole page back up to change shelf. It sticks under the topbar (whose real height the
   page writes into `--topbar-h`) and gets its own scrollbar, capped to the viewport; while the bar is
   tucked away on a scroll down, the rail moves up into the space it left. `overscroll-behavior` keeps
   a wheel at the end of the rail from scrolling the page underneath. The phone layout below turns
   it back into a horizontal strip. */
.items-shop { --rail-top: calc(var(--topbar-h, 4rem) + 0.6rem); }
body:has(.topbar.nav-hidden) .items-shop { --rail-top: 0.6rem; }
.items-shop .offer-cats {
  position: sticky; top: var(--rail-top); max-height: calc(100vh - var(--rail-top) - 0.6rem);
  overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin;
  transition: top 0.25s ease, max-height 0.25s ease;
}
.items-grid > p { grid-column: 1 / -1; }
/* A SHELF WITH NO CATEGORIES HAS NO RAIL, AND THE LIST MUST TAKE ITS PLACE. The rail is hidden then,
   which removes it from the grid, and the list fell into the rail's own 15rem column: skills,
   crafting stations and creatures (and every shelf on a free tier, which gets no categories) drew as
   one narrow column down the left. Spanning both columns gives it the width every other shelf has.
   …and scumsa's page draws NO rail at all for such a shelf, so its list is the shop's only child and
   fell into that same column: skills, creatures and garden plants as one or two narrow columns. */
.offer-shop > .offer-cats[hidden] + *, .offer-shop > :only-child { grid-column: 1 / -1; }
/* A grouped list (a skill under its attribute): each group is a heading over its own grid. */
.items-grid > .items-grp { grid-column: 1 / -1; min-width: 0; }
/* A family heading in the rail — Weapons, Gear, Base building. Not a filter: the database filters by
   one category at a time, so the group is a label over its categories. */
.items-rail-h {
  display: block; margin: 0.7rem 0 0.15rem; padding: 0 0.5rem;
  color: var(--faint); font-size: 0.68rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
}
.item-img { width: 100%; height: 100%; object-fit: contain; }
.item-img-x { display: flex; align-items: center; justify-content: center; color: var(--faint); }
.item-img-x .ico { width: 1rem; height: 1rem; }
.item-text { display: flex; flex-direction: column; gap: 0.12rem; min-width: 0; }
.item-name { color: var(--text); font-size: 0.92rem; line-height: 1.25; overflow-wrap: break-word; }
.item-chev { flex: none; width: 0.75rem; height: 0.75rem; color: var(--faint); }
/* The family it belongs to. `min-width: 0` so a long subcategory ("Modular building elements ·
   Walls") wraps inside the row rather than widening it. */
.item-meta {
  min-width: 0; font-size: 0.66rem; line-height: 1.25; color: var(--muted);
  letter-spacing: 0.02em; overflow-wrap: anywhere;
}
/* The game code an admin types. Monospace and faint: it is a handle, not a name. `anywhere`, because
   a code is one long word and a phone row is 260px. */
.item-code { min-width: 0; font-family: var(--mono); font-size: 0.66rem; color: var(--faint); overflow-wrap: anywhere; }
.items-foot { margin-top: 1.1rem; text-align: center; }

@media (max-width: 760px) {
  .offer-shop { grid-template-columns: minmax(0, 1fr); gap: 0.6rem; }
  /* A scrolling row on a phone: twenty categories down the side would push the rows a screen away. */
  .offer-cats { flex-direction: row; overflow-x: auto; position: static; padding-bottom: 0.35rem; scrollbar-width: thin; }
  .offer-cat { width: auto; flex: none; max-width: 11rem; border-color: var(--line); background: var(--inset); }
  .offer-cat.d1, .offer-cat.d2, .offer-cat.d3 { padding-left: 0.5rem; border-style: dashed; }
  .offer-cat-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* A family heading has no place in a row that scrolls sideways. */
  .items-rail-h { display: none; }
  .items-shop .offer-cats { position: static; max-height: none; overflow-x: auto; overflow-y: hidden; }
}

/* ── The record page ──────────────────────────────────────────────────────────────────────────
   The same page in the field console's modal and in the admin panel's record card: a hero with the
   big picture, the path, the name, the game's own description and the record's key facts; then the
   facts as a compact sheet; then every connection to another record, each a section of its own. */
.rp { display: flex; flex-direction: column; gap: 1.2rem; min-width: 0; }
.rp-hero {
  display: grid; grid-template-columns: var(--rp-art, 11rem) minmax(0, 1fr); gap: 1.3rem; align-items: start;
  padding-bottom: 1.15rem; border-bottom: 1px dashed var(--line-amber);
}
/* A record with nothing under its hero (a puppet: hit points, damage, code) ends on the hero, not on a rule over nothing. */
.rp-hero:last-child { padding-bottom: 0; border-bottom: 0; }
.rp-art { min-width: 0; }
.rp-art .item-detail-img {
  display: flex; align-items: center; justify-content: center; box-sizing: border-box;
  width: var(--rp-art, 11rem); height: var(--rp-art, 11rem); padding: 0.7rem; object-fit: contain;
  background: radial-gradient(circle at 50% 42%, var(--panel-2), var(--inset) 72%);
  border: 1px solid var(--line); border-radius: var(--radius-md, 6px);
}
.rp-art .item-img-x { color: var(--faint); }
.rp-art .item-img-x .ico { width: 2.4rem; height: 2.4rem; }
.rp-id { display: flex; flex-direction: column; gap: 0.6rem; min-width: 0; }
/* The path, each step a small chip that opens the database on that shelf — a chip rather than bare
   text, because a step that looks like a caption is a link nobody presses. Inline chips in a block
   line, not flex items: the path reads "Item · Ammunition · 5.56×45mm" as one line of text to a
   screen reader and to anything that copies it, and it wraps under the back button when it is long. */
/* A path, not a row of buttons: each step reads as a word of one line — "Item › Ranged weapons ›
   Assault rifle" — underlined faintly so it is plainly a link, amber under the pointer. Three pill
   boxes stacked on three lines of a phone were the most cluttered thing on the page. */
.rp-path {
  display: block; margin: 0; line-height: 1.6;
  font-family: var(--ui); font-size: 0.72rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--faint);
}
.rp-path .item-back, .rp-path .rec-back { margin: 0 0.5rem 0.2rem 0; vertical-align: middle; }
.rp-crumb {
  display: inline-flex; align-items: center; justify-content: center; vertical-align: baseline;
  margin: 0; padding: 0.05rem 0.15rem; cursor: pointer;
  background: transparent; border: 0; border-radius: var(--radius);
  color: var(--muted); font: inherit; font-weight: inherit; letter-spacing: inherit; text-transform: inherit; text-align: left;
  text-decoration: underline; text-decoration-color: var(--line); text-underline-offset: 3px;
  --hov-bg: transparent; --hov-fg: var(--amber-hi); --hov-line: transparent;
}
.rp-crumb:hover, .rp-crumb:focus-visible { background: var(--hov-bg); color: var(--hov-fg); border-color: var(--hov-line); text-decoration-color: currentColor; outline: none; transform: none; }
.rp-sep { color: var(--faint); margin: 0 0.15rem; }
.rp-step { white-space: nowrap; }
.rp-name { margin: 0; font-size: 1.6rem; font-weight: 700; line-height: 1.15; color: var(--text); overflow-wrap: anywhere; }
/* The game's own description fills the hero beside the picture: text inside a box fills the box. */
.rp .item-desc { margin: 0; max-width: none; color: var(--text); font-size: 0.9rem; line-height: 1.55; }
.rp .item-desc-none { color: var(--muted); font-size: 0.8rem; }
/* The record's own key facts, as small tiles in a row: rarity, weight, size, stack, the code. */
.rp .rp-keys { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0; }
.rp .rp-keys .ds {
  display: inline-flex; align-items: center; flex: 0 1 auto; min-width: 0; max-width: 100%;
  padding: 0.35rem 0.65rem; background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius);
}
/* The game code is a handle somebody copies, not a fact: no tile, a quiet monospace word at the end
   of the row, so the hero reads as rarity, weight and size rather than four equal boxes. */
.rp .rp-keys .ds.ds-raw { background: transparent; border-style: dashed; }
.rp .ds-raw .ds-v { font-family: var(--mono); font-weight: 400; font-size: 0.82rem; color: var(--muted); }
/* The facts, as a sheet rather than a wall of big boxes: a value over its label, small, many to a row. */
.rp-body { min-width: 0; }
.rp h3 {
  display: flex; align-items: baseline; gap: 0.35rem; margin: 1.15rem 0 0.5rem;
  color: var(--amber); font-size: 0.7rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase;
}
.rp-body > h3:first-child { margin-top: 0; }
.rp h3 .muted { color: var(--faint); letter-spacing: 0.04em; }
.rp .rp-body .dstats { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 12.5rem), 1fr)); gap: 0.35rem; }
.rp .rp-body .ds {
  display: flex; align-items: center; gap: 0.5rem; min-width: 0;
  padding: 0.4rem 0.65rem; background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius);
}
.rp .ds-txt { display: flex; flex-direction: column; gap: 0.1rem; line-height: 1.2; min-width: 0; }
/* A value in the UI face with figures that line up, its label small under it. Monospace made
   "Automatic Rifles" and "Weapon Cleaning Kit" look like code. */
.rp .ds-v { font-family: var(--ui); font-size: 0.95rem; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--text); overflow-wrap: anywhere; }
.rp .ds-k { font-size: 0.6rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.rp .price-buy .ds-v { color: var(--alarm); }
.rp .price-sell .ds-v { color: var(--online); }
/* The connections. Columns rather than a grid, so a short section ("Compatible ammo · 2") does not
   leave a hole beside a long one ("Used to craft · 116") — each card is as tall as itself. */
.rp-links { columns: 2 19rem; column-gap: 1rem; }
.rp-sec {
  display: inline-block; width: 100%; box-sizing: border-box; break-inside: avoid; margin: 0 0 1rem;
  padding: 0.75rem 0.85rem 0.85rem; background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius-md, 6px);
}
.rp .rp-sec > h3 { margin: 0 0 0.55rem; }
.rp-sec .rel-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 11.5rem), 1fr)); gap: 0.3rem; margin: 0; }
.rp-sec .rel-more { margin-top: 0.4rem; }
.rp-sec .rel-more > summary {
  cursor: pointer; padding: 0.2rem 0.1rem; color: var(--muted); font-size: 0.75rem;
  --hov-bg: transparent; --hov-fg: var(--text);
}
.rp-sec .rel-more > summary:hover { background: var(--hov-bg); color: var(--hov-fg); }
.rp-sec .rel-more[open] > summary { margin-bottom: 0.35rem; }
/* A linked record: its picture and its name, the whole tile a button into it. The name WRAPS rather
   than clipping — a cut name is a name a reader cannot use. */
.rp .rel-chip {
  display: flex; align-items: center; justify-content: flex-start; gap: 0.5rem; width: 100%; max-width: 100%; min-width: 0;
  margin: 0; padding: 0.3rem 0.5rem 0.3rem 0.3rem; cursor: pointer; text-align: left; text-decoration: none;
  background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--text); font-family: var(--ui); font-size: 0.8rem; font-weight: 400; letter-spacing: 0; line-height: 1.25;
  --hov-bg: var(--hover-bg); --hov-fg: var(--text); --hov-line: var(--line-amber);
}
.rp .rel-chip:hover, .rp .rel-chip:focus-visible { background: var(--hov-bg); color: var(--hov-fg); border-color: var(--hov-line); outline: none; transform: none; }
.rp .rel-chip img, .rp .rel-chip .rel-chip-x { flex: none; width: 2rem; height: 2rem; object-fit: contain; }
.rp .rel-chip .rel-chip-x { display: inline-flex; align-items: center; justify-content: center; color: var(--faint); }
.rp .rel-chip-n { min-width: 0; max-width: none; white-space: normal; overflow: visible; overflow-wrap: anywhere; }
.rp .rp-chip-t { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.rp .rp-chip-sub { color: var(--muted); font-size: 0.7rem; overflow-wrap: anywhere; }
.rp .rp-trader.out .rp-chip-sub { color: var(--alarm); }
.rp .rp-static { cursor: default; }
/* Words, not links: where a thing is found is a loot preset, which has no page and no picture of
   its own, so it is drawn as a quiet tag rather than as a tile that would open nothing. */
.rp-tags { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.rp-tag {
  display: inline-block; max-width: 100%; padding: 0.2rem 0.55rem; border: 1px solid var(--line); border-radius: 999px;
  background: var(--inset); color: var(--muted); font-size: 0.75rem; line-height: 1.35; overflow-wrap: anywhere;
}
/* A preset the game hangs on a creature or a plant carries that thing's picture, and a kind of
   container found in many places says how many — "Shelf ×20" is one answer, not twenty. */
.rp-tag-pic { display: inline-flex; align-items: center; gap: 0.35rem; padding-left: 0.25rem; }
.rp-tag-pic img { width: 1.35rem; height: 1.35rem; object-fit: contain; border-radius: 50%; background: var(--panel); flex: 0 0 auto; }
.rp-tag-n { color: var(--text); font-variant-numeric: tabular-nums; opacity: 0.75; }
/* The recipe rows inside a connection: an amount, then the alternatives as tiles. */
.rp-sec .craft-list + .craft-list { margin-top: 0.6rem; padding-top: 0.6rem; border-top: 1px dashed var(--line); }
.rp-sec .ing-row { display: grid; grid-template-columns: 2.2rem minmax(0, 1fr); align-items: start; gap: 0.4rem 0.5rem; margin: 0 0 0.35rem; }
.rp-sec .ing-amt { padding-top: 0.55rem; }
/* …and level with "any one of" when the row opens with it, rather than with the first tile under it. */
.rp-sec .ing-row:has(> .ing-or) .ing-amt { padding-top: 0; }
.rp-sec .ing-or { grid-column: 2; color: var(--muted); font-size: 0.7rem; }
.rp-sec .ing-opts { grid-column: 2; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 11rem), 1fr)); gap: 0.3rem; min-width: 0; }
.rp-sec .att-sub + .att-sub { margin-top: 0.5rem; }

/* ── UNDER THE HERO: TWO BALANCED COLUMNS, THEN "FOUND IN" ACROSS THE PAGE ─────────────────────
   Every card — facts and connections alike — in one flow, in the order `build()` decided, poured
   into two columns the browser BALANCES: a short card never leaves a hole beside a long one, and
   neither column runs on alone under an empty other. A card with a picture of its own (a recipe
   flow, a codex entry) spans both. One column where two would not fit (a phone, a narrow card). Then
   "Found in", the widest answer on the page, under all of it at the full width. */
.rp-body.rp-cols { display: block; columns: 2 22rem; column-gap: 0.9rem; }
.rp-body.rp-cols > * { display: block; box-sizing: border-box; width: 100%; margin: 0 0 0.9rem; break-inside: avoid; }
.rp-body.rp-cols > .rp-wide { column-span: all; }
.rp-links.rp-last { columns: auto; }
.rp-last > .rp-sec { display: block; margin: 0; }

@media (max-width: 640px) {
  .rp-hero { grid-template-columns: 5.5rem minmax(0, 1fr); gap: 0.9rem; }
  .rp-art .item-detail-img { width: 5.5rem; height: 5.5rem; padding: 0.35rem; }
  .rp-name { font-size: 1.25rem; }
  /* A full fingertip to press, and tighter type so a three-step path stays on one line. */
  .rp-path { letter-spacing: 0.04em; }
  .rp-crumb { min-height: 44px; min-width: 44px; padding: 0 0.2rem; }
  /* "Show all 30" is pressed with a thumb too. */
  .rp-sec .rel-more > summary { line-height: 44px; padding: 0 0.1rem; }
  /* Two facts to a row rather than one: a single column of 55px boxes is a screen of scrolling for
     eight short values. */
  .rp .rp-keys, .rp .rp-body .dstats { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 7.5rem), 1fr)); gap: 0.35rem; }
  .rp .rp-keys .ds { display: flex; }
  /* The picture and the name share the first row; the path, the description, the key facts and the
     admin's strip each take the whole width under them, so a long path wraps across the phone rather
     than down a 180px column beside the picture. */
  .rp-id { display: contents; }
  .rp-art { grid-column: 1; grid-row: 1; }
  .rp-name { grid-column: 2; grid-row: 1; align-self: center; }
  .rp-path { grid-column: 1 / -1; grid-row: 2; }
  /* Where the shell prints the name itself (the admin card's head) there is nothing to sit beside the
     picture: a path squeezed into that column stacks one step per line and pushes the card sideways,
     and the facts do not fit there either. So the hero is one column — the picture, then the path,
     the description and the facts at the full width of the card. */
  .rp-hero:not(:has(.rp-name)) { grid-template-columns: minmax(0, 1fr); }
  .rp .item-desc, .rp .rp-keys, .rp .rec-admin { grid-column: 1 / -1; }
}

/* ── STACKS: one thing and how many of it ──────────────────────────────────────────────────────
   The game draws a quantity on the picture, in the corner of the slot. Every "N of this" on a
   record page — a recipe's materials, a carcass, the contents of a box — is one of these. A slot
   that takes any one of several items is a `<details>` that opens in place to list them. */
.rp-stks { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 10.5rem), 1fr)); gap: 0.35rem; min-width: 0; }
.rp-stk {
  display: grid; grid-template-columns: 2.9rem minmax(0, 1fr); align-items: center; gap: 0.55rem;
  width: 100%; min-width: 0; margin: 0; padding: 0.3rem 0.5rem 0.3rem 0.3rem; box-sizing: border-box;
  background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--text); font-family: var(--ui); font-size: 0.8rem; font-weight: 400; letter-spacing: 0; line-height: 1.25;
  text-align: left; text-transform: none; cursor: pointer;
  --hov-bg: var(--hover-bg); --hov-fg: var(--text); --hov-line: var(--line-amber);
}
button.rp-stk:hover, button.rp-stk:focus-visible { background: var(--hov-bg); color: var(--hov-fg); border-color: var(--hov-line); outline: none; transform: none; }
.rp-stk-pic {
  position: relative; display: inline-grid; place-items: center; width: 2.9rem; height: 2.9rem;
  background: radial-gradient(circle at 50% 45%, var(--panel-2), var(--inset) 75%);
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
}
.rp-stk-pic img { width: 100%; height: 100%; object-fit: contain; padding: 0.15rem; box-sizing: border-box; }
.rp-stk-pic .rel-chip-x { color: var(--faint); }
.rp-stk-pic .rel-chip-x .ico { width: 1.1rem; height: 1.1rem; }
.rp-stk-n {
  position: absolute; right: 0.1rem; bottom: 0.05rem; padding: 0 0.15rem;
  background: var(--inset); border-radius: 3px;
  font-family: var(--mono); font-size: 0.68rem; font-weight: 700; line-height: 1.2; color: var(--amber-hi);
}
.rp-stk-t { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.rp-stk-name { min-width: 0; overflow-wrap: anywhere; }
.rp-stk-sub { color: var(--muted); font-size: 0.7rem; }
/* "any one of" — the summary is the slot, the body is every option. Open, it takes the whole row. */
details.rp-stk-any { display: block; padding: 0; }
details.rp-stk-any > summary {
  display: grid; grid-template-columns: 2.9rem minmax(0, 1fr); align-items: center; gap: 0.55rem;
  padding: 0.3rem 0.5rem 0.3rem 0.3rem; list-style: none; cursor: pointer; border-radius: var(--radius);
  --hov-bg: var(--hover-bg); --hov-fg: var(--text);
}
details.rp-stk-any > summary::-webkit-details-marker { display: none; }
details.rp-stk-any > summary:hover, details.rp-stk-any > summary:focus-visible { background: var(--hov-bg); color: var(--hov-fg); outline: none; }
details.rp-stk-any > summary .rp-stk-sub { color: var(--amber); text-decoration: underline dotted; text-underline-offset: 2px; }
details.rp-stk-any[open] { grid-column: 1 / -1; border-color: var(--line-amber); }
.rp-stk-opts {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 11rem), 1fr)); gap: 0.3rem;
  padding: 0.4rem; border-top: 1px dashed var(--line);
}

/* ── A RECIPE: what goes in → where → what comes out ───────────────────────────────────────────
   A container query, because the same block sits in a wide page body and in a narrow column
   beside it: a row where there is room, a column (the arrow pointing down) where there is not. */
.rcp { container-type: inline-size; min-width: 0; }
.rcp-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.6rem; min-width: 0; }
.rcp + .rcp { margin-top: 0.8rem; padding-top: 0.8rem; border-top: 1px dashed var(--line); }
.rcp-in, .rcp-out { min-width: 0; }
.rcp-in { display: flex; flex-direction: column; gap: 0.35rem; }
.rcp-lbl { display: block; margin-top: 0.15rem; color: var(--faint); font-size: 0.62rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; }
.rcp-mid { display: flex; flex-wrap: wrap; align-items: center; gap: 0.45rem 0.6rem; min-width: 0; }
.rcp-arrow { flex: none; display: inline-block; color: var(--amber); font-size: 1.4rem; line-height: 1; transform: rotate(90deg); }
.rcp-where {
  display: inline-block; padding: 0.15rem 0.55rem; border: 1px solid var(--line-amber); border-radius: 999px;
  color: var(--amber-hi); font-size: 0.75rem; font-weight: 600; white-space: nowrap;
}
.rcp-sts { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 10.5rem), 1fr)); gap: 0.3rem; flex: 1 1 12rem; min-width: 0; }
.rcp-out { display: flex; flex-direction: column; gap: 0.45rem; padding: 0.55rem; background: var(--panel-2); border: 1px solid var(--line-amber); border-radius: var(--radius-md, 6px); }
.rcp-out .rp-stks { grid-template-columns: minmax(0, 1fr); }
.rcp-meta { display: flex; flex-wrap: wrap; gap: 0.3rem 0.8rem; margin: 0; padding: 0; list-style: none; color: var(--text); font-size: 0.78rem; }
.rcp-meta li { display: inline-flex; align-items: center; gap: 0.3rem; min-width: 0; }
.rcp-meta .ico { flex: none; width: 0.85rem; height: 0.85rem; color: var(--amber); }
.rcp-mk { color: var(--muted); }
/* A meta line that opens a record (the skill a recipe trains): it reads as the line it is, underlined,
   and the hover is a ghost — no fill, the colour is the affordance — set as a PAIR on the element so
   the global `button:hover` amber cannot leave dark text on it. */
.rcp-meta .rcp-link {
  display: inline; margin: 0; padding: 0; border: 0; border-radius: 0; background: transparent; cursor: pointer;
  font: inherit; letter-spacing: inherit; text-transform: none; color: var(--text);
  text-decoration: underline; text-decoration-color: var(--line); text-underline-offset: 2px;
  --hov-bg: transparent; --hov-fg: var(--amber); --hov-line: transparent;
}
.rcp-meta .rcp-link:hover, .rcp-meta .rcp-link:focus-visible { background: var(--hov-bg); color: var(--hov-fg); border-color: var(--hov-line); outline: none; }
@container (min-width: 46rem) {
  .rcp-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(7rem, 12rem) minmax(13rem, 16rem); gap: 0.9rem; align-items: center; }
  .rcp-row .rcp-mid { flex-direction: column; flex-wrap: nowrap; justify-content: center; text-align: center; }
  .rcp-row .rcp-arrow { transform: none; font-size: 1.8rem; }
  .rcp-row .rcp-sts { flex: none; grid-template-columns: minmax(0, 1fr); width: 100%; }
}

/* ── THE FACTS, AS CARDS ─────────────────────────────────────────────────────────────────────────
   Each group of facts (Weapon, Nutrition, Durability…) is a card with the frame the connection
   sections have, so a record reads as a few labelled blocks. Two cards side by side where the body
   is wide enough; one column otherwise. A card that carries a picture of its own (a recipe flow)
   always takes the whole width. */
.rp-body { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 21rem), 1fr)); gap: 0.8rem; align-items: start; }
.rp-body > :not(.rp-grp) { grid-column: 1 / -1; }
.rp-grp {
  min-width: 0; box-sizing: border-box; padding: 0.75rem 0.85rem 0.85rem;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius-md, 6px);
}
.rp-grp.rp-wide { grid-column: 1 / -1; }
.rp .rp-grp > h3 { margin: 0 0 0.55rem; }
.rp .rp-body .rp-grp .dstats { grid-template-columns: repeat(auto-fill, minmax(min(100%, 9.5rem), 1fr)); }
.rp-grp .rec-note { margin: 0.55rem 0 0; }

/* A plant's growth stages, each segment as long as the stage takes. */
.rp-stages { display: flex; gap: 2px; margin: 0.7rem 0 0; padding: 0; list-style: none; min-width: 0; }
.rp-stages li {
  flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 0.1rem; padding: 0.35rem 0.45rem;
  background: var(--inset); border-top: 3px solid var(--online, var(--amber)); color: var(--text); font-size: 0.72rem;
}
.rp-stages li:first-child { border-radius: var(--radius) 0 0 var(--radius); }
.rp-stages li:last-child { border-radius: 0 var(--radius) var(--radius) 0; border-top-color: var(--amber); }
.rp-stage-n { overflow-wrap: anywhere; }
.rp-stage-h { color: var(--muted); font-family: var(--mono); font-size: 0.68rem; }

/* A quest's objectives: the journal's checklist, numbered, kind as a tag, count on the right. */
.rp-objs, .rp-deals { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 0.3rem; counter-reset: obj; }
.rp-obj, .rp-deals li {
  display: flex; align-items: baseline; gap: 0.55rem; min-width: 0; padding: 0.45rem 0.6rem;
  background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius); font-size: 0.85rem;
}
.rp-obj::before { counter-increment: obj; content: counter(obj); flex: none; color: var(--faint); font-family: var(--mono); font-size: 0.75rem; }
.rp-obj-k {
  flex: none; padding: 0 0.4rem; border: 1px solid var(--line-amber); border-radius: 999px;
  color: var(--amber-hi); font-size: 0.7rem;
}
.rp-obj .q-obj, .rp-deals .q-obj { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; color: var(--text); }
.rp-obj-n { flex: none; font-family: var(--mono); color: var(--amber-hi); }
.rp-grp .rec-sub { margin: 0.7rem 0 0.35rem; color: var(--muted); font-size: 0.68rem; letter-spacing: 0.08em; text-transform: uppercase; }
.rp-floors { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 0.35rem; }
.rp-floor { display: grid; grid-template-columns: 5rem minmax(0, 1fr); align-items: center; gap: 0.5rem; }
.rp-floor-n { color: var(--amber-hi); font-family: var(--mono); font-size: 0.75rem; }

/* A long list folded by kind: each fold's summary shows its name, its count and a few pictures. */
.rp-folds { display: flex; flex-direction: column; gap: 0.3rem; }
.rp-fold { border: 1px solid var(--line); border-radius: var(--radius); background: var(--inset); }
.rp-fold > summary {
  display: flex; align-items: center; gap: 0.6rem; min-width: 0; padding: 0.35rem 0.6rem; cursor: pointer; list-style: none;
  color: var(--text); font-size: 0.85rem; border-radius: var(--radius);
  --hov-bg: var(--hover-bg); --hov-fg: var(--text);
}
.rp-fold > summary::-webkit-details-marker { display: none; }
.rp-fold > summary::before { content: '▸'; flex: none; color: var(--amber); font-size: 0.75rem; }
.rp-fold[open] > summary::before { content: '▾'; }
.rp-fold > summary:hover, .rp-fold > summary:focus-visible { background: var(--hov-bg); color: var(--hov-fg); outline: none; }
.rp-fold-t { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.rp-fold-n { flex: none; font-family: var(--mono); font-size: 0.75rem; color: var(--amber-hi); }
.rp-fold-peek { flex: none; display: inline-flex; gap: 0.15rem; }
.rp-fold-peek img { width: 1.6rem; height: 1.6rem; object-fit: contain; }
.rp-fold > .rel-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 11.5rem), 1fr)); gap: 0.3rem; padding: 0.4rem; border-top: 1px dashed var(--line); }
@media (max-width: 480px) { .rp-fold-peek { display: none; } }

/* The database's rows carry a bigger, framed picture: in a catalogue the picture IS how most things
   are recognised, and 2.4rem of a rifle is a grey line. A trader's offer is the same catalogue, so its
   rows wear the same frame (it used to be the one list on the site with the small bare picture). */
.items-shop .item-card.offer-row, #offer-list .offer-row { grid-template-columns: 3rem minmax(0, 1fr) auto; }
.items-shop .item-card .offer-img, #offer-list .offer-img {
  width: 3rem; height: 3rem; box-sizing: border-box; padding: 0.15rem;
  background: radial-gradient(circle at 50% 45%, var(--panel-2), var(--inset) 75%); border: 1px solid var(--line);
}
@media (max-width: 640px) {
  /* A fingertip's 44px on everything that opens or folds on a record page. */
  .rp-fold > summary, details.rp-stk-any > summary { min-height: 44px; }
}

/* The codex: the game's own manual, set as an article at a reading width. Its paragraphs are body
   copy here, not the quoted-sentence rule `.rec-note` draws elsewhere, and its plates are framed. */
.rp .rp-codex .rec-note { max-width: none; margin: 0 0 0.75rem; padding: 0; border-left: 0; color: var(--text); font-size: 0.92rem; line-height: 1.6; }
.rp .rp-codex .rec-sub { max-width: none; margin: 1.2rem 0 0.35rem; color: var(--amber-hi); }
.rp .rp-codex .rec-fig { display: block; max-width: min(100%, 44rem); height: auto; margin: 0.4rem 0 1rem; border: 1px solid var(--line); border-radius: var(--radius-md, 6px); }

/* A creature or a vehicle is recognised by its render, which is a portrait in a square frame: on a
   wide page it gets a bigger frame than an item icon does. The phone hero keeps its own size. */
@media (min-width: 641px) {
  .rp[data-rp-dom="animals"], .rp[data-rp-dom="zombies"], .rp[data-rp-dom="npcs"], .rp[data-rp-dom="creatures"],
  .rp[data-rp-dom="vehicles"], .rp[data-rp-dom="fish"], .rp[data-rp-dom="codex"] { --rp-art: 15rem; }
}
.rp-stk.rp-stk-self { cursor: default; }

/* A place's sectors on the island's own 5 × 5 grid (the live map's rows and columns). */
.rp-sgrid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 3px; max-width: 18rem; }
.rp-sgrid-c {
  position: relative; display: grid; place-items: center; aspect-ratio: 1; min-width: 0;
  background: var(--inset); border: 1px solid var(--line); border-radius: 3px;
  color: var(--faint); font-family: var(--mono); font-size: 0.68rem;
}
.rp-sgrid-c.on { background: var(--panel-2); border-color: var(--amber); color: var(--amber-hi); font-weight: 700; }
.rp-sgrid-c b { position: absolute; right: 2px; bottom: 1px; font-size: 0.6rem; color: var(--text); }
/* ── THE ISLAND ON A RECORD PAGE ────────────────────────────────────────────────────────────────
   A place's placements and the places an item is found in, on the island the live map draws: the
   pyramid's top tile, the game's 5 × 5 sector grid over it with each sector shaded by how much of
   the record stands there, and each place a dot coloured by its kind. With dots the whole square is
   the control that opens the large map. No gap between the cells: a sector is exactly a fifth of
   the island each way and the dots are placed in percentages of the square. */
.rp-map { display: flex; flex-direction: column; gap: 0.45rem; min-width: 0; margin: 0; }
.rp-mapbox {
  position: relative; display: block; box-sizing: border-box; width: 100%; max-width: 26rem; aspect-ratio: 1;
  margin: 0; padding: 0; overflow: hidden; text-align: left; text-decoration: none;
  background: #070a10; border: 1px solid var(--line); border-radius: var(--radius-md, 6px);
  color: var(--text); font: inherit; letter-spacing: 0; text-transform: none;
  --hov-bg: #070a10; --hov-fg: var(--text); --hov-line: var(--line-amber);
}
.rp-map-go { cursor: zoom-in; }
.rp-map-go:hover, .rp-map-go:focus-visible { background: var(--hov-bg); color: var(--hov-fg); border-color: var(--hov-line); outline: none; transform: none; }
.rp-map-go:focus-visible { box-shadow: 0 0 0 2px var(--amber); }
.rp-mapbox .rp-mm-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0.9; pointer-events: none; }
.rp-sgrid.rp-map-grid {
  position: absolute; inset: 0; max-width: none; gap: 0;
  grid-template-columns: repeat(5, minmax(0, 1fr)); grid-template-rows: repeat(5, minmax(0, 1fr));
}
.rp-map-grid .rp-sgrid-c {
  aspect-ratio: auto; place-items: start; padding: 2px 4px; border: 0; border-radius: 0;
  background: transparent; box-shadow: inset -1px -1px 0 rgba(255, 255, 255, 0.13);
  color: rgba(255, 255, 255, 0.62); text-shadow: 0 1px 2px #000; font-size: 0.6rem; font-weight: 600;
}
.rp-map-grid .rp-sgrid-c.on { color: #fff; box-shadow: inset 0 0 0 1px rgba(240, 160, 40, 0.75); }
/* How much stands in a sector, as four steps of the same amber. */
.rp-h1, .rp-map-grid .rp-sgrid-c.rp-h1 { background-color: rgba(240, 160, 40, 0.16); }
.rp-h2, .rp-map-grid .rp-sgrid-c.rp-h2 { background-color: rgba(240, 160, 40, 0.28); }
.rp-h3, .rp-map-grid .rp-sgrid-c.rp-h3 { background-color: rgba(240, 160, 40, 0.42); }
.rp-h4, .rp-map-grid .rp-sgrid-c.rp-h4 { background-color: rgba(240, 160, 40, 0.58); }
.rp-mm-dot {
  position: absolute; z-index: 1; width: 10px; height: 10px; margin: -5px 0 0 -5px; border-radius: 50%;
  background: var(--badge-bg, #f0a028); border: 2px solid #0b0d10; box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.55);
}
/* Hundreds of places (a rifle is found in thirty kinds of them) — smaller dots, so the island shows. */
.rp-map-many .rp-mm-dot { width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px; border-width: 1.5px; }
/* An AREA the game authors (a hunting biome's circle, a fishing water's box), at its own size: a
   wash of the kind's colour with an edge, under the dots. Overlapping areas darken where they meet. */
.rp-mm-area {
  position: absolute; z-index: 1; box-sizing: border-box; pointer-events: none;
  background: color-mix(in srgb, var(--badge-bg, #f0a028) 26%, transparent);
  border: 1px solid color-mix(in srgb, var(--badge-bg, #f0a028) 80%, transparent);
}
.rp-mm-area.rp-mm-round { border-radius: 50%; }
.where-n { margin: 0 0 0.35rem; }
.where-note { margin: 0.35rem 0 0; }
.rp-map-cta {
  position: absolute; right: 6px; bottom: 6px; z-index: 2; display: inline-flex; align-items: center; gap: 0.3rem;
  padding: 0.22rem 0.55rem; border: 1px solid rgba(255, 255, 255, 0.18); border-radius: 999px;
  background: rgba(10, 13, 18, 0.88); color: #f3f4f6; font-size: 0.72rem; font-weight: 700; line-height: 1.2;
}
.rp-map-cta .ico { width: 14px; height: 14px; }
.rp-map-go:hover .rp-map-cta, .rp-map-go:focus-visible .rp-map-cta { border-color: rgba(240, 160, 40, 0.8); }
/* One colour per kind of place, the same on the small map, its key, the rows and the large map. */
.rpk-0 { --badge-bg: #f0a028; }
.rpk-1 { --badge-bg: #4cc9f0; }
.rpk-2 { --badge-bg: #9be15d; }
.rpk-3 { --badge-bg: #f472b6; }
.rpk-4 { --badge-bg: #b49cff; }
.rpk-5 { --badge-bg: #ff8a65; }
.rpk-6 { --badge-bg: #2dd4bf; }
.rpk-7 { --badge-bg: #facc15; }
.rpk-8 { --badge-bg: #c8a27a; }
.rpk-9 { --badge-bg: #7c93ff; }
.rpk-x { --badge-bg: #d4d4d8; }
/* A kind of place's colour is its FAMILY's (PLACE_FAMILY in itemRecord.js), the same on every record:
   where people lived, military, underground, industry, medical, law, air, trade, hazards, nature. */
.rpf-settle { --badge-bg: #9be15d; }
.rpf-military { --badge-bg: #ff8a65; }
.rpf-under { --badge-bg: #b49cff; }
.rpf-industry { --badge-bg: #facc15; }
.rpf-medical { --badge-bg: #4cc9f0; }
.rpf-law { --badge-bg: #7c93ff; }
.rpf-air { --badge-bg: #2dd4bf; }
.rpf-trade { --badge-bg: #f0a028; }
.rpf-hazard { --badge-bg: #f472b6; }
.rpf-nature { --badge-bg: #c8a27a; }
.rpf-other { --badge-bg: #d4d4d8; }
/* A kind's badge: its pictogram (PLACE_ICON in itemRecord.js) painted dark on its colour, the same
   plate the large map's markers and layer switches draw. A row whose kind has no place on the island
   keeps the pictogram on a neutral plate, because its colour would name a dot that is not there. */
.rp-kico {
  display: inline-flex; align-items: center; justify-content: center; flex: none; width: 1.15rem; height: 1.15rem;
  border-radius: 50%; background: var(--badge-bg, #d4d4d8); border: 1px solid rgba(0, 0, 0, 0.55);
}
.rp-kico .ico { width: 0.78rem; height: 0.78rem; color: #0b0d10; stroke-width: 2; }
.rp-kico.rp-kico-off { background: var(--panel-2, #1b2028); border-color: var(--line, #2a313b); }
.rp-kico.rp-kico-off .ico { color: var(--muted, #9aa3ad); }
/* A few places on the small island draw their pictogram too; a crowd stays dots. */
.rp-mm-dot.rp-mm-ico {
  display: flex; align-items: center; justify-content: center; width: 16px; height: 16px; margin: -8px 0 0 -8px;
  border-width: 1px; border-color: rgba(0, 0, 0, 0.6); box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.45), 0 1px 3px rgba(0, 0, 0, 0.6);
}
.rp-mm-dot.rp-mm-ico .ico { width: 10px; height: 10px; color: #0b0d10; stroke-width: 2.2; }
.rp-kdot { display: inline-block; flex: none; width: 0.62rem; height: 0.62rem; border-radius: 50%; background: var(--badge-bg); border: 1px solid rgba(0, 0, 0, 0.55); }
.rp-map-key { display: flex; flex-wrap: wrap; gap: 0.25rem 0.85rem; max-width: 26rem; color: var(--muted); font-size: 0.74rem; line-height: 1.3; }
.rp-map-key > .rar-key { flex: 1 1 100%; }
.rp-map-k { display: inline-flex; align-items: center; gap: 0.35rem; min-width: 0; overflow-wrap: anywhere; }
.rp-map-k b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.rp-hk { display: inline-flex; flex: none; border: 1px solid var(--line); border-radius: 2px; overflow: hidden; }
.rp-hk i { display: block; width: 0.55rem; height: 0.7rem; }
/* What tells two same-named chips apart (the sector a contract is hunted in), under the name. */
.rel-chip-sub { display: block; color: var(--muted); font-size: 0.7rem; font-weight: 400; }

/* ── THE BODY, THE MEDICINE AND THE SKILLS ─────────────────────────────────────────────────────
   A treatment table is how many items a wound needs at each stage, per Medical skill level; a skill
   table is what each level changes. Both are real tables (rows of one thing, columns of one scale)
   and both can be wider than a phone, so the TABLE scrolls inside its own box and the page never
   does. Numbers are tabular so a column of them lines up. */
.rp-tbl-wrap { max-width: 100%; overflow-x: auto; margin: 0.2rem 0 0; }
.rp-tbl { border-collapse: collapse; width: 100%; font-size: 0.8rem; font-variant-numeric: tabular-nums; }
.rp-tbl caption { caption-side: top; text-align: left; padding: 0 0 0.35rem; color: var(--muted); font-size: 0.72rem; }
.rp-tbl th, .rp-tbl td { padding: 0.32rem 0.45rem; border-bottom: 1px solid var(--line); text-align: center; white-space: nowrap; }
.rp-tbl thead th { color: var(--amber); font-family: var(--mono); font-size: 0.7rem; font-weight: 700; letter-spacing: 0.04em; white-space: normal; vertical-align: bottom; }
.rp-tbl tbody th { text-align: left; color: var(--muted); font-weight: 400; white-space: normal; min-width: 7rem; }
.rp-tbl td { font-family: var(--mono); color: var(--text); }
.rp-tbl td.rp-all { color: var(--text); }
.rp-tbl .rp-tbl-time th, .rp-tbl .rp-tbl-time td { color: var(--muted); }
.rp-tbl thead th.rp-tbl-corner { text-align: left; color: var(--muted); font-family: var(--ui); font-weight: 400; letter-spacing: 0; }
.rp-lvl-tbl tbody th { min-width: 9rem; }
.rp-treat-items tbody th { min-width: 11rem; }
.rp-treat-items .rp-stk { padding: 0.15rem 0.35rem 0.15rem 0.15rem; grid-template-columns: 2rem minmax(0, 1fr); }
.rp-treat-items .rp-stk-pic { width: 2rem; height: 2rem; }
.rp-treat + .rp-treat { margin-top: 0.9rem; padding-top: 0.8rem; border-top: 1px dashed var(--line); }
.rp-treat > .rel-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 11.5rem), 1fr)); gap: 0.3rem; }
.rp-treat-how { margin: 0.45rem 0 0.1rem; color: var(--amber-hi); font-size: 0.75rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; }
.rp-note { margin: 0.45rem 0 0; color: var(--muted); font-size: 0.78rem; line-height: 1.4; }
/* Cause → symptoms → treatment: three columns where there is room, one under another on a phone. */
.rp-flow-row { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr) auto minmax(0, 1fr); gap: 0.6rem; align-items: start; }
.rp-flow-row:has(> .rp-flow-col:nth-of-type(2):last-child) { grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); }
.rp-flow-col { min-width: 0; }
.rp-flow-col .rel-list { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.3rem; margin-top: 0.35rem; }
.rp-flow-arrow { align-self: center; color: var(--amber); font-size: 1.2rem; }
@media (max-width: 760px) {
  .rp-flow-row, .rp-flow-row:has(> .rp-flow-col:nth-of-type(2):last-child) { grid-template-columns: minmax(0, 1fr); }
  .rp-flow-arrow { transform: rotate(90deg); justify-self: start; padding-left: 0.6rem; }
}
/* A condition's stages, C1 the mildest. The life-threatening ones are marked in the alarm colour. */
.rp-cstages { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 4.6rem), 1fr)); gap: 0.3rem; margin: 0; padding: 0; list-style: none; }
.rp-cstages li {
  display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; padding: 0.4rem 0.5rem;
  background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--muted); font-size: 0.7rem; overflow-wrap: anywhere;
}
.rp-cstages li b { font-family: var(--mono); font-size: 0.95rem; color: var(--text); }
.rp-cstages li.lt { border-color: var(--alarm); }
.rp-cstages li.lt b, .rp-cstages li.lt span:first-of-type { color: var(--alarm); }

/* WHERE IT IS FOUND — place first. The island as its sector grid, each sector shaded by how many of
   the places it spawns in stand there; then one row per kind of place (the place's own page is the
   link), what it lies in there, and the rarity the game draws it at as six pips and a word. */
.fi { display: flex; flex-direction: column; gap: 0.6rem; }
.fi-rows { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr)); gap: 0.35rem; }
.fi-row, .fi-sub { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; padding: 0.4rem 0.5rem;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--inset); }
.fi-top { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.3rem 0.6rem; min-width: 0; }
.rp .fi-pl { display: inline-flex; align-items: center; min-width: 0; max-width: 100%; margin: 0; padding: 0;
  background: none; border: 0; color: var(--amber-hi); font-family: var(--ui); font-size: 0.86rem; font-weight: 700;
  text-align: left; letter-spacing: 0; cursor: pointer; overflow-wrap: anywhere;
  --hov-bg: transparent; --hov-fg: var(--text); --hov-line: transparent; }
.rp .fi-pl:hover, .rp .fi-pl:focus-visible { background: var(--hov-bg); color: var(--hov-fg); border-color: var(--hov-line); text-decoration: underline; outline: none; transform: none; }
.rp .fi-pl.fi-pl-x { color: var(--text); cursor: default; text-decoration: none; }
/* The rarity pips and word are `.rar-odds` in rarity.css, the one look both surfaces share. */
.fi-ins { display: flex; flex-wrap: wrap; gap: 0.25rem; }
.fi-in { padding: 0.08rem 0.45rem; border: 1px solid var(--line); border-radius: 999px; background: var(--panel); color: var(--text); font-size: 0.74rem; line-height: 1.35; overflow-wrap: anywhere; }
.fi-meta { display: flex; flex-wrap: wrap; gap: 0.2rem 0.7rem; color: var(--muted); font-size: 0.72rem; }
.fi-sec { font-family: var(--mono); overflow-wrap: anywhere; }
.fi-n { font-variant-numeric: tabular-nums; }
.fi-sub .rel-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 11.5rem), 1fr)); gap: 0.3rem; }
.fi-note, .fi-also { margin: 0; }
@media (max-width: 640px) {
  .rp .fi-pl:not(.fi-pl-x) { min-height: 44px; }
}
/* The answer to "where do I get it" is the widest one on the page: it spans the connection columns,
   so its place rows can sit side by side instead of in one long column. */
.rp-links > .rp-sec[data-rp-sec="found-in"]:has(> .fi) { display: block; column-span: all; }

/* ── TRADING: the trader's price, who sells it, who buys it, what players paid ──────────────────
   One card, its parts divided by the dashed rule the hero uses and headed in the muted small caps
   the record's sub-headings use, so it reads as one answer in four parts rather than four cards. */
.rp-trade .rp-trade-px { margin: 0; }
.rp-tsub { min-width: 0; margin-top: 0.85rem; padding-top: 0.75rem; border-top: 1px dashed var(--line); }
.rp-trade > h3 + .rp-tsub { margin-top: 0; padding-top: 0; border-top: 0; }
.rp .rp-th {
  display: flex; align-items: baseline; gap: 0.35rem; margin: 0 0 0.5rem;
  color: var(--muted); font-size: 0.66rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
}
.rp .rp-th .muted { color: var(--faint); letter-spacing: 0.04em; }
.rp-tsub .rel-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 11.5rem), 1fr)); gap: 0.3rem; margin: 0; }
/* The market part sizes itself by its OWN width, not the window's: in a column of the two-column
   page it is half the window wide, and a trade row laid out for a full-width card squeezes the
   counter's name to nothing there. */
.rp-mkt { container-type: inline-size; }
@container (max-width: 30rem) {
  .rp-mkt .ith { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "act price" "sub when"; }
  .rp-mkt .ith-price { text-align: right; }
}

/* ── WHAT PLAYERS PAID ──────────────────────────────────────────────────────────────────────────
   The last price each way, the price over time and the newest trades, in the frame every other card
   on the page has. The chart's lines are an SVG stretched to the plot (strokes kept one width by
   `vector-effect`); its points and their labels are HTML placed in percentages, so a circle stays a
   circle however wide the card is. Bought is the alarm colour and sold the online one, as on the
   Economy tab. */
.rp .rp-mkt .mkt-sum { grid-template-columns: repeat(auto-fill, minmax(min(100%, 11rem), 1fr)); margin: 0 0 0.75rem; }
.mkt-tr { margin-left: 0.3rem; color: var(--muted); font-family: var(--ui); font-size: 0.7rem; font-weight: 700; white-space: nowrap; }
.mkt-chart { display: grid; grid-template-columns: auto minmax(0, 1fr); grid-template-rows: 9.5rem auto; gap: 0.25rem 0.5rem; margin: 0.15rem 0 0.35rem; }
.mkt-y { position: relative; grid-row: 1; grid-column: 1; min-width: 3rem; color: var(--muted); font-family: var(--mono); font-size: 0.64rem; }
.mkt-y span { position: absolute; right: 0; transform: translateY(-50%); white-space: nowrap; }
.mkt-plot { position: relative; grid-row: 1; grid-column: 2; min-width: 0; background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius); }
.mkt-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.mkt-grid { stroke: var(--line); stroke-width: 1; stroke-dasharray: 3 4; }
.mkt-line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; opacity: 0.85; }
.mkt-line-buy { stroke: var(--alarm); }
.mkt-line-sell { stroke: var(--online); }
.mkt-pt { position: absolute; z-index: 1; box-sizing: border-box; width: 8px; height: 8px; margin: -4px 0 0 -4px; border-radius: 50%; border: 1.5px solid var(--inset); }
.mkt-pt-buy { background: var(--alarm); }
.mkt-pt-sell { background: var(--online); }
.mkt-pt.mkt-last { z-index: 2; width: 12px; height: 12px; margin: -6px 0 0 -6px; box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.14); }
.mkt-pt-v {
  position: absolute; right: -2px; bottom: 100%; margin-bottom: 5px; padding: 0.05rem 0.35rem;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 3px;
  color: var(--text); font-family: var(--mono); font-size: 0.68rem; font-weight: 600; line-height: 1.35; white-space: nowrap;
}
.mkt-pt.mkt-below .mkt-pt-v { bottom: auto; top: 100%; margin: 5px 0 0; }
.mkt-x { grid-row: 2; grid-column: 2; display: flex; justify-content: space-between; gap: 0.5rem; color: var(--muted); font-family: var(--mono); font-size: 0.64rem; }
.mkt-legend { display: flex; flex-wrap: wrap; align-items: center; gap: 0.2rem 0.9rem; margin: 0 0 0.7rem; color: var(--muted); font-size: 0.72rem; }
.mkt-lg { display: inline-flex; align-items: center; gap: 0.35rem; }
.mkt-lg i { display: inline-block; width: 0.9rem; height: 3px; border-radius: 2px; }
.mkt-lg-buy i { background: var(--alarm); }
.mkt-lg-sell i { background: var(--online); }
.mkt-lg-unit { margin-left: auto; }
.rp-mkt .item-history { list-style: none; display: flex; flex-direction: column; gap: 0.25rem; max-height: none; margin: 0; padding: 0; overflow: visible; }
.rp-mkt .ith {
  display: grid; grid-template-columns: 6.2rem minmax(4.5rem, auto) minmax(0, 1fr) auto; grid-template-areas: "act price sub when";
  align-items: baseline; gap: 0.15rem 0.75rem; margin: 0; padding: 0.42rem 0.65rem;
  background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius); font-size: 0.82rem;
}
.rp-mkt .ith-act { grid-area: act; min-width: 0; font-family: var(--ui); font-size: 0.7rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; }
.rp-mkt .ith-buy .ith-act { color: var(--alarm); }
.rp-mkt .ith-sell .ith-act { color: var(--online); }
.rp-mkt .ith-act em { margin-left: 0.25rem; color: var(--muted); font-style: normal; text-transform: none; letter-spacing: 0; }
.rp-mkt .ith-price { grid-area: price; color: var(--text); font-family: var(--mono); font-weight: 600; white-space: nowrap; }
.rp-mkt .ith-sub { grid-area: sub; display: flex; flex-wrap: wrap; gap: 0 0.65rem; min-width: 0; margin: 0; color: var(--muted); font-size: 0.76rem; }
.rp-mkt .ith-who { color: var(--text); overflow-wrap: anywhere; }
.rp-mkt .ith-trader { overflow-wrap: anywhere; }
.rp-mkt a.ith-trader {
  color: var(--amber-hi); text-decoration: underline; text-decoration-color: var(--line); text-underline-offset: 2px;
  --hov-bg: transparent; --hov-fg: var(--text); --hov-line: transparent;
}
.rp-mkt a.ith-trader:hover, .rp-mkt a.ith-trader:focus-visible { background: var(--hov-bg); color: var(--hov-fg); border-color: var(--hov-line); outline: none; }
.rp-mkt .ith-when { grid-area: when; color: var(--muted); font-size: 0.74rem; white-space: nowrap; text-align: right; }
.rp-mkt .mkt-more { margin-top: 0.35rem; }
.rp-mkt .mkt-more > summary { cursor: pointer; padding: 0.2rem 0.1rem; color: var(--muted); font-size: 0.75rem; --hov-bg: transparent; --hov-fg: var(--text); }
.rp-mkt .mkt-more > summary:hover { background: var(--hov-bg); color: var(--hov-fg); }
.rp-mkt .mkt-more[open] > summary { margin-bottom: 0.35rem; }
.mkt-empty, .mkt-note { margin: 0.4rem 0 0; }
@media (max-width: 640px) {
  .rp-mkt .ith { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "act price" "sub when"; }
  .rp-mkt .ith-price { text-align: right; }
  .rp-mkt .mkt-more > summary { line-height: 44px; padding: 0 0.1rem; }
  /* A fingertip of slop above and below the counter's name, not a taller line: the row stays one line. */
  .rp-mkt a.ith-trader { position: relative; }
  .rp-mkt a.ith-trader::before { content: ''; position: absolute; inset: -12px 0; }
  .mkt-chart { grid-template-rows: 8rem auto; }
}

/* WHERE IT IS FOUND, beside its map: the island on the left, the kinds of place on the right; one
   column where there is no room for two. */
.fi-geo { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.8rem 1.1rem; }
.fi-geo > .rp-map { flex: 1 1 15rem; max-width: 26rem; }
.fi-geo > .fi-list { flex: 999 1 18rem; min-width: 0; }
.fi-list .fi-rows { grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr)); }
.fi-pl-w { display: inline-flex; align-items: center; gap: 0.4rem; min-width: 0; max-width: 100%; }

/* A recipe's skill link on a phone is a fingertip tall. Not slop: the product's picture sits 8px
   above the meta row, and a reach across that gap would hand a finger aimed at the product the
   skill instead. The line grows to fit; last in the file, after everything that sizes the row. */
@media (max-width: 640px) {
  .rcp-meta .rcp-link { display: inline-flex; align-items: center; min-height: 40px; }
}

/* ── ONE SHEET OF FACTS ON A PHONE ─────────────────────────────────────────────────────────────
   Two short facts to a row everywhere on a record — the hero's and every card's — rather than one
   55px box per line. Last in the file so it outranks the wider minimums above it. */
@media (max-width: 640px) {
  .rp .rp-keys, .rp .rp-body .dstats, .rp .rp-body .rp-grp .dstats { grid-template-columns: repeat(auto-fill, minmax(min(100%, 6.6rem), 1fr)); }
  .rp-grp, .rp-sec { padding: 0.6rem 0.65rem 0.7rem; }
}
/* The notes the page adds under a section are quiet: the game's own words above them are the content. */
.rp .rp-note, .rp .fi-note, .rp .fi-also { color: var(--muted); font-size: 0.74rem; line-height: 1.4; }

/* ── THE TOOLBAR OVER EVERY DATABASE ───────────────────────────────────────────────────────────
   Search, then the shelf picker, in one row on all three surfaces (the field console, the admin
   panel, scumsa.com); one under the other on a phone, both the full width. Each surface keeps its
   own control styles; this is only their layout. */
.cat-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.cat-toolbar > :first-child { flex: 1 1 16rem; min-width: 0; }
.cat-toolbar > :not(:first-child) { flex: 0 1 15rem; min-width: 0; }
@media (max-width: 640px) {
  .cat-toolbar > :not(:first-child) { flex: 1 1 100%; }
}

/* ── MARKERS ON TOP OF EACH OTHER (mapStack.js) ──────────────────────────────────────────────────
   Every map in the product: the top marker of a stack carries its count as a badge, and a press on
   a stack opens a chooser listing every marker in it. The badge lives in the canvas, so it
   counter-scales with `--mz` like the dot it sits on; the chooser floats over the map in screen
   space. scumsa.com's /map/ carries the same two rules in map.css, in its own tokens. */
.map-mk[data-stack]::after {
  content: attr(data-stack); position: absolute; left: 0; top: 0; z-index: 3; pointer-events: none;
  transform-origin: 0 0; transform: scale(var(--mz, 1)) translate(5px, -19px);
  min-width: 15px; height: 15px; padding: 0 3px; box-sizing: border-box; border-radius: 999px;
  background: var(--amber); color: var(--on-amber, #111); border: 1px solid rgba(0, 0, 0, 0.6);
  font-family: var(--ui); font-size: 10px; font-weight: 700; line-height: 13px; text-align: center;
}
.map-chooser {
  position: absolute; z-index: 30; min-width: 11rem; max-width: min(18rem, calc(100% - 12px)); max-height: min(18rem, calc(100% - 12px));
  overflow-y: auto; overscroll-behavior: contain; padding: 0.35rem; box-sizing: border-box;
  background: var(--panel); border: 1px solid var(--line-amber); border-radius: var(--radius-md, 6px);
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.55); color: var(--text);
}
.map-chooser-h { margin: 0.1rem 0.35rem 0.3rem; color: var(--muted); font-family: var(--ui); font-size: 0.68rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; }
.map-chooser-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.map-chooser-it {
  display: flex; align-items: center; justify-content: flex-start; gap: 0.5rem; width: 100%; min-height: 36px; margin: 0;
  padding: 0.25rem 0.45rem; border: 0; border-radius: var(--radius); cursor: pointer; text-align: left;
  background: transparent; color: var(--text); font-family: var(--ui); font-size: 0.82rem; font-weight: 500; letter-spacing: 0; text-transform: none;
  --hov-bg: var(--hover-bg); --hov-fg: var(--amber-hi); --hov-line: transparent;
}
.map-chooser-it:hover, .map-chooser-it:focus-visible { background: var(--hov-bg); color: var(--hov-fg); border-color: var(--hov-line); outline: none; transform: none; }
.map-chooser-ic {
  flex: none; display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px;
  border-radius: 50%; border: 1px solid rgba(0, 0, 0, 0.55);
}
.map-chooser-ic svg { width: 12px; height: 12px; color: inherit; }
.map-chooser-n { min-width: 0; overflow-wrap: anywhere; }
@media (max-width: 640px) { .map-chooser-it { min-height: 44px; } }

/* ── THE ARMOURY LOOK ───────────────────────────────────────────────────────────────────────────
   The database in the game's own visual language: a shelf is a wall of inventory cells (a dark
   square, the picture filling it, the name under it), a record leads with a large picture, a
   figure carries a thin bar showing where it sits among its own kind, and a weapon's parts are
   laid out as SLOTS, each a box of picture tiles. Our graphite and amber, not anybody's red. */

/* A shelf: square cells. The grouped shelf's own grid (`.items-grp .offer-rows`) gets the same. */
.items-grid.offer-rows, .items-grid .items-grp > .offer-rows {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 8.4rem), 1fr)); gap: 0.5rem; column-gap: 0.5rem;
}
.items-grid .item-card.offer-row {
  position: relative; display: flex; flex-direction: column; align-items: stretch; gap: 0; padding: 0; min-width: 0;
  background: linear-gradient(180deg, var(--panel-2, var(--panel)), var(--inset));
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; text-align: left;
  transition: border-color 0.12s;
}
.items-grid .item-card.offer-row:hover { border-color: var(--line-amber); }
.items-grid .item-card .offer-img {
  /* A FIXED height, not `aspect-ratio`: a cell whose height follows its width moves every row below it
     when the page loses its scrollbar (a window opening locks the scroll), and the reader's place moves with it. */
  display: flex; width: 100%; height: 7.4rem; padding: 0.6rem; box-sizing: border-box; border-radius: 0;
  background: radial-gradient(circle at 50% 44%, rgba(255, 255, 255, 0.06), transparent 68%), var(--inset);
}
.items-grid .item-card .offer-img .ico { width: 1.6rem; height: 1.6rem; }
.items-grid .item-card .item-text { padding: 0.4rem 0.5rem 0.5rem; border-top: 1px solid var(--line); flex: 1 1 auto; }
.items-grid .item-card .item-name { font-size: 0.8rem; font-weight: 600; line-height: 1.2; overflow-wrap: anywhere; }
.items-grid .item-card .item-meta { font-size: 0.62rem; }
.items-grid .item-card .item-chev { position: absolute; top: 0.4rem; right: 0.4rem; }

/* The hero: a big picture in a framed cell, the frame's corners marked in amber. */
.rp-art .item-detail-img { position: relative; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.35); }
@media (min-width: 641px) {
  .rp[data-rp-dom="items"], .rp[data-rp-dom="building"] { --rp-art: 15rem; }
}

/* A figure with its place among its own kind: label, value, then a thin track under both. The fill
   is the published range (`data-min`/`data-max`), never more than the track. */
.ds.ds-bar { flex-wrap: wrap; align-items: flex-end; row-gap: 0.45rem; }
.ds.ds-bar .ds-txt { flex: 1 1 auto; }
.ds-track { display: block; flex: 1 0 100%; height: 3px; border-radius: 2px; background: var(--line); overflow: hidden; }
.ds-fill { display: block; height: 100%; max-width: 100%; min-width: 2px; background: var(--amber); border-radius: 2px; }

/* A weapon's slots: one box per slot, each a grid of picture tiles that open the part. */
.att-slots { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr)); gap: 0.55rem; margin-top: 0.4rem; }
.att-slots .att-slot { margin: 0; padding: 0.5rem; min-width: 0; background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius); }
.att-slots .att-sub-h { color: var(--amber); font-weight: 700; }
.att-slots .rel-list.att-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 4.6rem), 1fr)); gap: 0.35rem; margin: 0.2rem 0 0; }
.att-tiles .rel-chip { flex-direction: column; justify-content: flex-start; gap: 0.25rem; padding: 0.35rem 0.25rem; text-align: center; min-width: 0; background: var(--panel-2, var(--panel)); }
.att-tiles .rel-chip img, .att-tiles .rel-chip .rel-chip-x { width: 100%; height: 2.8rem; }
.att-tiles .rel-chip-n { white-space: normal; max-width: 100%; font-size: 0.66rem; line-height: 1.15; overflow-wrap: anywhere; }
.att-slots .rel-more { margin-top: 0.3rem; }

/* ── ONE COLUMN OF FIGURES, LIKE THE GAME'S OWN STAT SHEET ─────────────────────────────────────
   Every figure on a record is one line: its label on the left, the value on the right, and — where
   the game files it among others of its kind — a thin bar under both. One column rather than a grid
   of tiles, so a label in a long language has the whole width of its card and is never cut. Last in
   the file so it outranks every grid above it, phone rules included. */
.rp .rp-keys, .rp .rp-body .dstats, .rp .rp-body .rp-grp .dstats { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0; }
.rp .rp-keys { max-width: 30rem; }
.rp .rp-keys .ds, .rp .rp-body .ds {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.3rem 0.6rem; min-width: 0; max-width: none;
  padding: 0.45rem 0.15rem; background: transparent; border: 0; border-bottom: 1px solid var(--line); border-radius: 0;
}
.rp .rp-keys .ds.ds-raw { border-style: solid; }
.rp .rp-keys .ds:last-child, .rp .rp-body .dstats > .ds:last-child { border-bottom: 0; }
/* `wrap-reverse` with `row-reverse`: when a label and its value cannot share a line, the label keeps
   the line above and the value drops under it on the right, so no word of a label is ever split. */
.rp .ds .ds-txt { flex: 1 1 100%; flex-flow: row-reverse wrap-reverse; justify-content: space-between; align-items: baseline; gap: 0.15rem 0.75rem; }
.rp .ds .ds-k { flex: 1 1 auto; overflow-wrap: break-word; font-size: 0.66rem; line-height: 1.3; }
.rp .ds .ds-v { flex: 0 1 auto; min-width: 0; max-width: 100%; margin-left: auto; text-align: right; font-size: 1.02rem; line-height: 1.2; }
.att-slots { align-items: start; }
.rp .ds.ds-bar .ds-track { height: 4px; margin: 0.1rem 0 0.05rem; background: var(--inset); box-shadow: inset 0 0 0 1px var(--line); }
@media (min-width: 1100px) { .rp-hero { --rp-art: 15rem; } }
/* An item's size as the game's inventory cells: one small square per cell, the same grid the
   player sees in their pockets, so "7 x 3" is a shape at a glance rather than two numbers. */
.rp .ds.ds-grid { align-items: center; }
.rp .ds.ds-grid .ds-txt { flex: 1 1 0; min-width: 0; }
.ds-cells { --cell: 0.5rem; display: grid; gap: 2px; flex: 0 0 auto; padding: 2px; background: var(--inset); border: 1px solid var(--line); border-radius: 2px; }
.ds-cells i { display: block; width: var(--cell); height: var(--cell); background: var(--amber); opacity: 0.55; border-radius: 1px; }
/* A cell's corners, as the game's inventory cell draws them: the size top left, the weight bottom
   right, small and on a dark plate so they read over any picture. */
.item-card .offer-img { position: relative; }
.item-corner {
  position: absolute; z-index: 1; padding: 0.05rem 0.25rem; border-radius: 2px; pointer-events: none;
  background: color-mix(in srgb, var(--bg) 78%, transparent); color: var(--text);
  font-family: var(--ui); font-size: 0.62rem; font-weight: 600; line-height: 1.2; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.item-corner.item-size { top: 0.2rem; left: 0.2rem; color: var(--muted); }
.item-corner.item-wt { right: 0.2rem; bottom: 0.2rem; }
