/* ---- THE INVENTORY WINDOW, one size up ---------------------------------------------------------
   20% over the shared panel: the window itself (880 -> 1056), the paperdoll slots and stage, the
   bag tiles (they inherit the extra width through the 8-column grid) and the panel's text. Scoped
   to the bag so the other ledgers keep the shared measure. */
/* THE BAG'S CELL SIZE at the panel's base width, measured rather than guessed: an eight-column
   `1fr` grid inside a 1056px panel computes to 68.8px cells with an 8px gap (`tools/_uimeasure`,
   2026-09-06). It is written down because the packed width below is derived FROM it. */
:root { --inv-bag-cell: 68.8px; }
.rpg-panel--inv { width: min(1056px, 94vw); }
/* THE WINDOW GROWS SIDEWAYS FOR A PACK; IT DOES NOT SQUEEZE.
   The bag's tracks are `1fr`, so a ninth column plus its gutter divided the SAME panel width into
   more tracks and every cell shrank — measured 68.8px -> 58px, a 16% loss on every icon in the bag
   for the crime of putting a backpack on. Reported as the window scaling rather than growing.
   The panel is widened by exactly what one more column costs, and that is TWO gaps rather than one:
   the packed grid is ten tracks (eight cells, a gutter, the pack column), so it has nine gaps where
   the plain grid has seven. Counting one gap left the cells 0.9px short of where they started —
   measured, which is the only way that arithmetic ever gets settled. */
.rpg-panel--inv.is-packed {
  width: min(calc(1056px + var(--inv-bag-cell) + var(--sp-2) * 2 + var(--sp-3)), 96vw);
}
.rpg-panel--inv .rpg-inv { grid-template-columns: 360px minmax(0, 1fr); }
.rpg-panel--inv .rpg-doll .rpg-slot { width: 62px; }
.rpg-panel--inv .rpg-doll-stage { min-height: 293px; }
.rpg-panel--inv .rpg-panel-title { font-size: 36px; }
.rpg-panel--inv .rpg-panel-sub { font-size: 17px; }
.rpg-panel--inv .rpg-group-title { font-size: 13px; }
.rpg-panel--inv .rpg-tab { font-size: 12px; }
.rpg-panel--inv .rpg-tile { font-size: 29px; }
.rpg-panel--inv .rpg-slot .rpg-slot-tag { font-size: 10px; }
.rpg-panel--inv .rpg-count { font-size: 12px; line-height: 18px; min-width: 18px; }
.rpg-panel--inv .rpg-purse { font-size: 17px; }
.rpg-panel--inv .rpg-cap { font-size: 14px; }
.rpg-panel--inv .rpg-bag-search { font-size: 17px; }

/* EMPTY BAG. Forty blank cells is the biggest thing on the panel and, unlabelled, it reads as a
   grid that failed to load. The line is laid OVER the grid rather than replacing it, so the cells
   are still there to drop onto and the layout does not jump the moment the first item arrives.
   Quiet enough that it disappears the instant there is anything to look at. */
.rpg-bag { position: relative; }
.rpg-bag.is-empty::after {
  content: 'Nothing carried yet — loot, gather and trade to fill it.';
  position: absolute; inset: 0; display: grid; place-items: center; pointer-events: none;
  padding: 0 var(--sp-4); text-align: center;
  font-family: var(--font-quill, Georgia, serif); font-style: italic; font-size: 13px;
  color: var(--ink-mute); text-shadow: 0 1px 6px rgba(0,0,0,.85); }
/* The cells fade back so the sentence is the thing being read, not competing with 40 rectangles. */
.rpg-bag.is-empty .rpg-cell { opacity: .45; }
/* THE LIGHT PLATE — the item ground, and the actual fix for dark-on-dark icons.
 *
 * `references/base items 1.png`, which is the project's own item sheet, draws every item as ink-
 * contoured art on a WHITE ground with a soft cast shadow. The panel was doing the opposite:
 * transparent mesh renders on a near-black cell, with a CSS glow around each silhouette
 * compensating. That glow was a workaround for having no ground at all — it lit the cell AROUND the
 * item instead of separating the item from it, and it looked like what it was.
 *
 * Inverting the ground removes the problem rather than patching it: the nine icons measured under
 * 3:1 against the dark cell are dark-brown and near-black meshes, which is exactly what a pale plate
 * separates best. It is one rule for every icon, present and future, and nothing here is per-item —
 * the constraint that ruled out hand-tuned per-icon fixes still holds.
 *
 * ONLY OCCUPIED CELLS GET IT, and that falls out of the markup rather than needing a modifier:
 * `InventoryPanel` emits `.rpg-tile` only when there is an item, so forty empty cells stay dark and
 * the grid keeps reading as a dark panel with lit items in it, not as a wall of white squares.
 *
 * Cool grey rather than the reference's pure white: at full white, forty tiles glare against the
 * violet panel. This carries a little of the panel's own hue so the plate belongs to the UI, and it
 * is a gradient with an inset shade so the tile reads as a recessed plate rather than a flat block. */
.rpg-tile { position: absolute; inset: 4px; border-radius: var(--r-1); display: grid; place-items: center;
  font-size: 24px; }
/* Rendered gear icons (public/icons/*.png) — fill the tile, keep aspect, sit above the glass.
 *
 * LEGIBILITY RIM. The cell composites to rgb(14,10,33) (`--glass-deep` over the panel), and a
 * measured sweep of every icon in public/icons/ found nine whose mean contrast against it is under
 * 3:1 — the leather set worst at 1.10:1 (chest_leather), then boots/gloves 1.18, bramble_husk 1.24,
 * dagger_beaten 1.82, the fishing rod's shaft, wolf/vole pelt, the buckler and the bow. Those are
 * dark-brown and near-black meshes on a near-black cell; they vanish.
 *
 * The rim is stacked drop-shadows, NOT a change to the cell and NOT a change to the meshes:
 *  - Lightening the cell does not work and was measured: an item at L≈0.009 against a cell lifted
 *    to L≈0.012 gets a WORSE ratio, because the two move together. Only a near-grey plate reaches
 *    3:1, and that is a different panel.
 *  - Brightening the materials would change how the items look in the world too.
 *  - It reads from the icon's own ALPHA, so it survives `node tools/icon-shots.mjs` re-rendering
 *    every PNG from its mesh — nothing here is per-item, and there is nothing for a re-render to
 *    undo. That is the constraint a hand-tuned per-icon crop or curve would fail.
 * THE RIM IS GONE, AND SO IS THE PLATE. Three attempts lived here — a blurred halo, a hard 1px
 * stroke, then a pale plate behind every tile — and all three were the panel trying to rescue an
 * icon that had no edge of its own.
 *
 * The icons now carry a baked ink contour, rendered into the PNG from the mesh (see the icon branch
 * of `capture.ts`). That is how Ragnarok's inventory reads: the icon owns its outline, so it works
 * on any ground and the cell is free to be whatever the panel wants — which here means the dark
 * glass it always was. One rule, applied once at render time, for every item present and future.
 *
 * What is left is a soft CAST SHADOW, which sits the item on the cell instead of floating over it. */
/* BRIGHTNESS LIFT. The icons are rendered from the world meshes, and the dark sets (leather,
   iron, pelts) carry the world's shade with them — legible on the ground, murky at 40px. One
   global lift here rather than per-icon edits or brighter materials: it survives every
   `icon-shots.mjs` re-render and never touches how the items look in the world. */
/* ONE FILTER FOR EVERY PLACE AN ICON IS DRAWN (`--icon-filter`). The item card used to carry its
   own — no lift, and a heavy 60% black drop-shadow — so the same webp read noticeably darker in the
   card than in the bag it was hovered from. Both now read this variable. */
:root { --icon-filter: brightness(1.38) contrast(1.04) saturate(1.08) drop-shadow(0 2px 2px rgba(34, 26, 66, .38)); }
.rpg-icon { width: 100%; height: 100%; object-fit: contain; filter: var(--icon-filter); }
/* STACK COUNT. Sat at `right:4px; bottom:2px` as bare text, which put it exactly on the tile's
   rarity rim (an inset ring at the same 4px inset) — so on any item above common the digits
   collided with the coloured line and the last one looked clipped, worst on 3-digit stacks.
   It is now a pill that sits INSIDE the rim with its own backing, so it reads on any icon and at
   any rarity, and `line-height`/`padding` keep the glyphs clear of their own box. */
.rpg-count { position: absolute; right: 5px; bottom: 5px; z-index: 2; pointer-events: none;
  min-width: 15px; padding: 0 4px; border-radius: 999px;
  font-size: 10px; line-height: 15px; text-align: center;
  font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-weight: 600;
  color: var(--ink); background: rgba(6, 8, 20, .82);
  box-shadow: 0 0 0 1px rgba(255,255,255,.10), 0 1px 3px rgba(0,0,0,.6); }
.rpg-drag-ghost { position: fixed; z-index: 60; pointer-events: none; font-size: 26px;
  filter: drop-shadow(0 4px 6px rgba(0,0,0,.6)); transform: translate(-50%,-50%); }
.rpg-drag-ghost .rpg-icon { width: 38px; height: 38px; }
/* RARITY RIM. One `--rarity` custom property per tier, set on the cell, so the rim, the cell's own
   border and its glow all read from a single hue instead of three rules drifting apart — and so the
   equipped SLOTS get the same treatment the bag cells always had (an equipped legendary used to
   look identical to an equipped common). Hues come from items.css, which is the palette of record. */
.rpg-cell[data-rarity="uncommon"],  .rpg-slot[data-rarity="uncommon"]  { --rarity: var(--r-uncommon-c); }
.rpg-cell[data-rarity="rare"],      .rpg-slot[data-rarity="rare"]      { --rarity: var(--r-rare-c); }
.rpg-cell[data-rarity="epic"],      .rpg-slot[data-rarity="epic"]      { --rarity: var(--r-epic-c); }
.rpg-cell[data-rarity="legendary"], .rpg-slot[data-rarity="legendary"] { --rarity: var(--r-legendary-c); }
/* Rarity touches the OUTER BORDER COLOUR and nothing else. The old treatment stacked an inner
   ring on the tile plus a hue wash through the background, which read as a double border and a
   tinted cell; both are gone. One rule, one border, the light plate stays #e6e4ed for every tier. */
.rpg-cell[data-rarity], .rpg-slot[data-rarity] { border-color: var(--rarity, var(--glass-stroke)); }
.rpg-cell[data-rarity]:hover, .rpg-slot[data-rarity]:hover { border-color: var(--rarity); }

/* tooltip parts — the .rpg-tip box itself is defined once, further down */
.rpg-tip h4 { margin: 0 0 1px; font-family: var(--font-banner); color: var(--tip-accent, var(--gold-hi)); }
.rpg-tip .stat { color: var(--sage); font-family: var(--font-mono); font-size: 12px; }
.rpg-tip .flavor { color: var(--ink-quill); font-style: italic; margin-top: 4px; }
/* Rarity accent. ONE pair of custom properties per tier, taken from items.css so the bag, the shop
   and the concept sheets cannot disagree about what a tier looks like. Every child that wants the
   colour reads `--tip-accent`; nothing hard-codes a hex. */
.rpg-tip                          { --tip-accent: var(--r-common-c);    --tip-aura: var(--r-common-g); }
.rpg-tip[data-rarity="uncommon"]  { --tip-accent: var(--r-uncommon-c);  --tip-aura: var(--r-uncommon-g); }
.rpg-tip[data-rarity="rare"]      { --tip-accent: var(--r-rare-c);      --tip-aura: var(--r-rare-g); }
.rpg-tip[data-rarity="epic"]      { --tip-accent: var(--r-epic-c);      --tip-aura: var(--r-epic-g); }
.rpg-tip[data-rarity="legendary"] { --tip-accent: var(--r-legendary-c); --tip-aura: var(--r-legendary-g); }
.rpg-tip[data-rarity] { border-color: color-mix(in srgb, var(--tip-accent) 35%, var(--glass-stroke-hi)); }
.rpg-tip .rpg-tip-tier { display: flex; align-items: center; gap: 6px; margin-bottom: 6px;
  font-family: var(--font-banner); font-weight: 500; font-size: 11px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--tip-accent); }
.rpg-tip .rpg-tip-tier::before { content: ""; width: 5px; height: 5px; border-radius: 50%;
  background: var(--tip-accent); box-shadow: 0 0 6px var(--tip-accent); }
/* Level gate — sits under a hairline like the item card's footer. Ember when the character is
   below it, which is the whole point of showing it. */
.rpg-tip .rpg-tip-req { margin-top: 6px; padding-top: 5px; border-top: 1px solid var(--glass-stroke);
  font-family: var(--font-mono); font-weight: 500; font-variant-numeric: tabular-nums; font-size: 11px; letter-spacing: .04em; color: var(--ink-soft); }
.rpg-tip .rpg-tip-req.is-locked { color: var(--ember); }

/* character sheet */
.rpg-stats { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-5); }
.rpg-statrow { display: flex; align-items: center; justify-content: space-between;
  padding: var(--sp-2) var(--sp-3); background: var(--glass); border-radius: var(--r-2); margin-bottom: var(--sp-2); }
.rpg-statrow .val { font-family: var(--font-mono); color: var(--ink); }
.rpg-plus { width: 24px; height: 24px; border-radius: 50%; border: 1px solid var(--gold-deep);
  background: transparent; color: var(--gold); cursor: pointer; }
.rpg-plus:disabled { opacity: .25; cursor: default; }
.rpg-chip { display: inline-block; font-size: 11px; font-family: var(--font-mono); color: var(--ink-mute);
  border: 1px solid var(--glass-stroke); border-radius: var(--r-pill); padding: 1px 8px; margin: 2px; }

/* skill tree */
.rpg-tree { position: relative; display: grid; gap: var(--sp-6) var(--sp-5); justify-content: center; }
.rpg-tree svg.rpg-links { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
/* Prerequisite lines (SkillPanel.drawLinks): faint until met, then lit in the branch colour. */
.rpg-tree svg.rpg-links line { stroke: var(--glass-stroke, rgba(255,255,255,.18)); stroke-width: 3;
  stroke-linecap: round; }
.rpg-tree svg.rpg-links line.is-met { stroke: var(--branch, var(--gold)); opacity: .85;
  filter: drop-shadow(0 0 3px var(--branch, var(--gold))); }
/* Branch headings for a branched tree (the Mage's elements): a coloured label with an underline. */
.rpg-tree-branch { color: var(--branch, var(--gold)); font-size: 12px; font-weight: 700;
  letter-spacing: .14em; border-bottom: 2px solid var(--branch, var(--gold)); padding-bottom: 3px;
  align-self: center; }
.rpg-tree--branched { column-gap: 14px; margin-bottom: 26px; }
.rpg-tree--branched .rpg-node.has-ranks { border-color: var(--branch, var(--gold));
  box-shadow: 0 0 12px var(--branch, var(--gold-glow)); }
/* Column headings, drawn as a row INSIDE the tree grid (row 2 — below the Crafting root, above the
   first discipline tier) rather than a separate grid stacked above the whole thing. See the note in
   WorkerPanel.ts for why: the old placement put the headings above the root, which then read as one
   of the branches instead of as what unlocks all of them. */
.rpg-tree-head { align-self: end; font-size: 10px; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--gold); text-align: center; }
.rpg-node { position: relative; width: 64px; height: 64px; border-radius: 50%; z-index: 1;
  background: var(--glass-deep); border: 2px solid var(--glass-stroke); color: var(--ink-soft);
  display: grid; place-items: center; cursor: pointer; font-size: 22px; }
.rpg-node.has-ranks { border-color: var(--gold); color: var(--gold-hi); box-shadow: 0 0 12px var(--gold-glow); }
.rpg-node.is-locked { opacity: .4; cursor: not-allowed; }
.rpg-node .rank { position: absolute; bottom: -6px; right: -6px; font-size: 11px; font-family: var(--font-mono);
  background: var(--space-4); border-radius: var(--r-pill); padding: 0 6px; border: 1px solid var(--glass-stroke); }

/* player hp bar (projected under hero) */
.rpg-hpbar { position: fixed; z-index: 35; width: 84px; height: 9px; transform: translate(-50%,0);
  border: 1px solid rgba(7,9,26,.8); border-radius: var(--r-pill); background: rgba(7,9,26,.6);
  overflow: hidden; pointer-events: none; box-shadow: 0 2px 6px rgba(0,0,0,.5); }
.rpg-hpbar .fill { position: absolute; inset: 0; border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--hp-deep), var(--hp)); transition: width .15s; transform-origin: left; }
.rpg-hpbar.is-hidden { display: none; }

