/* --- Shop overlay: full-viewport, big vendor image over the live scene + centred window --- */
.rpg-shop { position: fixed; inset: 0; z-index: 50; display: none; pointer-events: none; }
.rpg-shop.is-open { display: block; pointer-events: auto;
  background: radial-gradient(120% 90% at 50% 40%, rgba(7,9,26,.28), rgba(7,9,26,.55)); }
/* Large vendor portrait composited on the bottom-LEFT, rising from the bottom, beside the window.
   It was on the right, with the window pushed left to clear it — which put the preview card (parked
   off the window's right edge) straight behind the portrait (owner, 2026-09-03). Window centred
   like every other window, portrait left, card right: the three never share a column. Every
   vendor in the game opens this one window, so this is the layout of every shop with an NPC. */
.rpg-shop-vendor { position: absolute; left: 0; right: auto; bottom: 0; height: min(84vh, 760px);
  max-width: 30vw; object-fit: contain; object-position: bottom left; pointer-events: none;
  filter: drop-shadow(0 10px 26px rgba(0,0,0,.55)); z-index: 1; }
/* Window centred in the left ~62% of the viewport (clears the right-side portrait): wares (left) + checkout (right). */
/* SIX WARES WIDE, at a FIXED height. 900px carries six `.rpg-cell` columns plus the 260px cart at
   the same tile size four columns had in 720px; the height is fixed rather than a max so the window
   stops growing and shrinking as the cart fills or the tab flips — the wares grid scrolls inside it
   (owner, 2026-09-03). */
.rpg-shop-window { position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%); z-index: 2;
  width: min(900px, 66vw); height: min(640px, 88vh); display: flex; flex-direction: column;
  border: 2px solid transparent; border-radius: var(--r-4); padding: var(--sp-5);
  background:
    var(--frame-corners),
    linear-gradient(180deg, rgba(20,17,30,.96), rgba(12,10,22,.97)) padding-box,
    var(--frame-trim) border-box;
  box-shadow: var(--frame-inner-glow), 0 30px 90px rgba(0,0,0,.7); }
/* The close button is `.rpg-close` now — the shared one, in the shared place. The private copy
   that used to sit here differed only in being 2px higher, which is precisely the kind of
   difference that makes two windows read as two different games. */
.rpg-shop-title { margin-bottom: var(--sp-3); }
.rpg-shop-title .rpg-panel-title { font-size: 26px; }
.rpg-shop-title .rpg-panel-sub { margin-bottom: var(--sp-2); }
.rpg-shop-gold { font-family: var(--font-mono); font-size: 14px; color: var(--ink); }
.rpg-shop-gold i { color: var(--gold); font-style: normal; }
/* `.rpg-tab` is the shared tab, so the strip only has to say where it sits and where its rule is —
   the same underline treatment the bag's and the bestiary's carry. */
.rpg-shop-tabs { display: flex; gap: 4px; margin-bottom: var(--sp-3);
  border-bottom: 1px solid var(--glass-stroke); }
.rpg-shop-cols { display: grid; grid-template-columns: minmax(0,1fr) 260px; gap: var(--sp-4); min-height: 0; flex: 1; }
/* THE WARES ARE THE INVENTORY'S OWN TILES. Four columns of `.rpg-cell`, each with its name and
   price beneath — an item looks the same wherever a player meets it, which is the rule every other
   window already followed and this one did not. The tiles come from `.rpg-cell` (rarity edge,
   aspect-ratio 1, the same 44px box), so nothing here sizes an item by hand. */
.rpg-shop-wares { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--sp-3) var(--sp-2); align-content: start; overflow: auto; padding: var(--sp-2) 4px; min-height: 0;
  background: var(--glass-deep); border: 1px solid var(--glass-stroke); border-radius: var(--r-2); }
.rpg-shop-ware { display: flex; flex-direction: column; align-items: center; gap: 3px; min-width: 0; }
/* THE SHOP'S PREVIEW CARD: over the shop overlay (z 50, where the card's own 41 put it BEHIND the
   dimmed scene and out of sight), and parked off the shop window's right edge — the window is
   centred and min(900px, 66vw) wide, so its edge is 50% + half of that, plus the gutter. The
   portrait is on the LEFT, so nothing can stand between the card and the player. */
/* TWO CLASSES, NOT ONE. `.rpg-itemcard`'s own rule is declared 500 lines BELOW this one, at the
   same specificity, so a single-class override here lost the cascade and the card stayed at
   z-index 41 — behind the overlay, exactly as reported twice. Doubling the selector makes the
   override win wherever it is written. */
.rpg-itemcard.rpg-shop-card { z-index: 51; }
.rpg-shop-ware .rpg-cell { width: 100%; }
.rpg-shop-ware-name { max-width: 100%; font-size: 11px; color: var(--ink-soft);
  text-align: center; line-height: 1.2;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.rpg-shop-ware-price { font-family: var(--font-mono); font-size: 11px;
  font-variant-numeric: tabular-nums; color: var(--gold); }
.rpg-shop-ware-price i, .rpg-shop-total i { font-style: normal; color: var(--gold); }
/* THE OVER-LEVEL BADGE reuses the bag's stack pip, so "Lv 7" sits where a count would — one badge
   position in the whole interface rather than a second one invented here. */
.rpg-shop-ware .rpg-cell.is-filtered { cursor: default; }
.rpg-shop-ware-icon { width: 26px; height: 26px; flex: 0 0 auto; display: grid; place-items: center; }
.rpg-shop-ware-icon img { width: 26px; height: 26px; object-fit: contain; }
.rpg-shop-checkout { display: flex; flex-direction: column; min-height: 0;
  background: var(--glass-deep); border: 1px solid var(--glass-stroke); border-radius: var(--r-2); padding: var(--sp-3); }
.rpg-shop-checkout-head { font-family: var(--font-banner); font-size: 13px; letter-spacing: .05em;
  text-transform: uppercase; color: var(--gold-hi); margin-bottom: var(--sp-2); }
.rpg-shop-cart { list-style: none; margin: 0; padding: 0; overflow: auto; flex: 1; min-height: 40px; }
.rpg-shop-cart-line { display: grid; grid-template-columns: 26px 1fr auto; grid-template-areas: 'ic nm del' 'ic qty pr';
  gap: 2px 6px; align-items: center; padding: 4px 0; border-bottom: 1px solid var(--glass-stroke); }
.rpg-shop-cart-line .rpg-shop-ware-icon { grid-area: ic; width: 26px; height: 26px; }
.rpg-shop-cart-line .rpg-shop-ware-icon img { width: 26px; height: 26px; }
.rpg-shop-cart-name { grid-area: nm; font-size: 12px; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rpg-shop-qty { grid-area: qty; display: flex; align-items: center; gap: 6px; }
.rpg-shop-qty button { width: 18px; height: 18px; line-height: 1; padding: 0; border-radius: 4px;
  border: 1px solid var(--glass-stroke); background: var(--glass-hi); color: var(--ink); cursor: pointer; }
.rpg-shop-qty .q { font-family: var(--font-mono); font-size: 12px; min-width: 14px; text-align: center; }
.rpg-shop-cart-price { grid-area: pr; font-family: var(--font-mono); font-weight: 500; font-variant-numeric: tabular-nums; font-size: 11px; color: var(--gold); text-align: right; }
.rpg-shop-cart-del { grid-area: del; justify-self: end; width: 18px; height: 18px; padding: 0; border-radius: 4px;
  border: 1px solid var(--glass-stroke); background: transparent; color: var(--ink-soft); cursor: pointer; font-size: 11px; }
.rpg-shop-checkout-foot { margin-top: var(--sp-2); }
.rpg-shop-total { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 6px; font-size: 12px; color: var(--ink-soft); }
.rpg-shop-total .amt { font-family: var(--font-mono); font-size: 15px; color: var(--ink); }
.rpg-shop-total .amt.is-short { color: var(--ember); }
.rpg-shop-confirm { width: 100%; padding: 8px 0; border-radius: var(--r-2); border: 1px solid var(--gold);
  background: linear-gradient(180deg, var(--gold), var(--gold-deep, #b8860b)); color: #14110a; font-weight: 600; cursor: pointer; }
.rpg-shop-confirm:disabled { opacity: .5; cursor: default; filter: grayscale(.4); }
.rpg-shop-clear { width: 100%; margin-top: 6px; padding: 5px 0; border-radius: var(--r-2);
  border: 1px solid var(--glass-stroke); background: transparent; color: var(--ink-soft); cursor: pointer; font-size: 12px; }
.rpg-shop-clear:disabled { opacity: .4; cursor: default; }
.rpg-shop-empty { color: var(--ink-quill); font-style: italic; padding: 10px; text-align: center; font-size: 12px; }

/* --- Dialogue overlay: a low, wide conversation box over the LIVE scene (no backdrop) ---
   Same rule as the shop: nothing opaque covers the world, so talking to someone reads as
   leaning in rather than leaving the game. Bottom-centred and clear of the toolbar. */
.rpg-dialogue { position: fixed; inset: 0; z-index: 50; display: none; pointer-events: none; }
.rpg-dialogue.is-open { display: block; pointer-events: auto; }
.rpg-dialogue-window { position: absolute; left: 50%; bottom: 96px; transform: translateX(-50%);
  width: min(720px, 84vw); pointer-events: auto;
  border: 2px solid transparent; border-radius: var(--r-4); padding: var(--sp-5) var(--sp-6);
  background:
    var(--frame-corners),
    linear-gradient(180deg, rgba(20,17,30,.96), rgba(12,10,22,.97)) padding-box,
    var(--frame-trim) border-box;
  box-shadow: var(--frame-inner-glow), 0 30px 90px rgba(0,0,0,.7); }
.rpg-dialogue-close { position: absolute; top: 12px; right: 14px; width: 30px; height: 30px; border-radius: 50%;
  background: var(--glass-hi); border: 1px solid var(--glass-stroke); color: var(--ink-soft); cursor: pointer; }
.rpg-dialogue-close:hover { color: var(--ember); border-color: var(--ember); }
.rpg-dialogue-speaker { font-family: var(--font-display); font-size: 24px; color: var(--gold-hi); }
.rpg-dialogue-quest { font-family: var(--font-banner); font-size: 11px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink-soft); margin-top: 2px; }
.rpg-dialogue-text { font-family: var(--font-quill); font-size: 15px; line-height: 1.6;
  color: var(--ink); margin: var(--sp-3) 0 0; }
.rpg-dialogue-progress { font-family: var(--font-mono); font-size: 13px; color: var(--gold);
  margin-top: var(--sp-2); }
.rpg-dialogue-missing { font-family: var(--font-quill); font-size: 13px; color: var(--ink-soft);
  margin: var(--sp-2) 0 0; padding-left: 1.2em; }
.rpg-dialogue-actions { display: flex; gap: var(--sp-2); justify-content: flex-end; margin-top: var(--sp-4); }
.rpg-dialogue-act { padding: 8px 22px; border-radius: var(--r-2); border: 1px solid var(--gold);
  background: var(--glass-hi); color: var(--gold-hi); font-family: var(--font-banner);
  letter-spacing: .05em; font-size: 13px; cursor: pointer; }
.rpg-dialogue-act:hover { box-shadow: 0 0 14px var(--gold-glow); }
.rpg-dialogue-bye { padding: 8px 18px; border-radius: var(--r-2); border: 1px solid var(--glass-stroke);
  background: transparent; color: var(--ink-soft); font-family: var(--font-banner);
  letter-spacing: .05em; font-size: 13px; cursor: pointer; }
.rpg-dialogue-bye:hover { color: var(--ink); border-color: var(--ink-soft); }

/* --- Job changer overlay: Kessa's conversation, a LIST OF CHOICES ---
   Same shape and idiom as the dialogue box above (no backdrop, bottom-centred over the live
   scene, same frame), because it is the same kind of moment — leaning in to talk to someone.
   It is a separate block only because the body is a catalogue of rows rather than one speech. */
.rpg-jobs { position: fixed; inset: 0; z-index: 50; display: none; pointer-events: none; }
.rpg-jobs.is-open { display: block; pointer-events: auto; }
.rpg-jobs-window { position: absolute; left: 50%; bottom: 96px; transform: translateX(-50%);
  width: min(720px, 84vw); pointer-events: auto;
  border: 2px solid transparent; border-radius: var(--r-4); padding: var(--sp-5) var(--sp-6);
  background:
    var(--frame-corners),
    linear-gradient(180deg, rgba(20,17,30,.96), rgba(12,10,22,.97)) padding-box,
    var(--frame-trim) border-box;
  box-shadow: var(--frame-inner-glow), 0 30px 90px rgba(0,0,0,.7); }
.rpg-jobs-close { position: absolute; top: 12px; right: 14px; width: 30px; height: 30px; border-radius: 50%;
  background: var(--glass-hi); border: 1px solid var(--glass-stroke); color: var(--ink-soft); cursor: pointer; }
.rpg-jobs-close:hover { color: var(--ember); border-color: var(--ember); }
.rpg-jobs-title { font-family: var(--font-display); font-size: 24px; color: var(--gold-hi); }
.rpg-jobs-body { margin-top: var(--sp-3); }
.rpg-jobs-intro, .rpg-jobs-current { font-family: var(--font-quill); font-size: 15px; line-height: 1.6;
  color: var(--ink); margin: 0; }
/* The permanence warning is the one line in this window that must not read as flavour text. */
.rpg-jobs-warn { font-family: var(--font-quill); font-size: 15px; line-height: 1.6;
  color: var(--ember); margin: 0; }
.rpg-jobs-row { margin-top: var(--sp-4); padding-top: var(--sp-3);
  border-top: 1px solid var(--glass-stroke); }
.rpg-jobs-name { font-family: var(--font-banner); font-size: 13px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--gold); }
.rpg-jobs-blurb { font-family: var(--font-quill); font-size: 14px; line-height: 1.55;
  color: var(--ink-soft); margin: var(--sp-1) 0 var(--sp-3); }
.rpg-jobs-take { padding: 8px 22px; border-radius: var(--r-2); border: 1px solid var(--gold);
  background: var(--glass-hi); color: var(--gold-hi); font-family: var(--font-banner);
  letter-spacing: .05em; font-size: 13px; cursor: pointer; }
.rpg-jobs-take:hover:not(:disabled) { box-shadow: 0 0 14px var(--gold-glow); }
/* Ineligible jobs stay VISIBLE and dimmed — the button says the level you are working toward. */
.rpg-jobs-take:disabled { opacity: .4; cursor: default; border-color: var(--glass-stroke);
  color: var(--ink-soft); }
.rpg-jobs-actions { display: flex; gap: var(--sp-2); justify-content: flex-end; margin-top: var(--sp-4); }
.rpg-jobs-confirm { padding: 8px 22px; border-radius: var(--r-2); border: 1px solid var(--gold);
  background: var(--glass-hi); color: var(--gold-hi); font-family: var(--font-banner);
  letter-spacing: .05em; font-size: 13px; cursor: pointer; }
.rpg-jobs-confirm:hover { box-shadow: 0 0 14px var(--gold-glow); }
.rpg-jobs-cancel { padding: 8px 18px; border-radius: var(--r-2); border: 1px solid var(--glass-stroke);
  background: transparent; color: var(--ink-soft); font-family: var(--font-banner);
  letter-spacing: .05em; font-size: 13px; cursor: pointer; }
.rpg-jobs-cancel:hover { color: var(--ink); border-color: var(--ink-soft); }

/* ── CHAT ────────────────────────────────────────────────────────────────────────────────────
   Bottom-left furniture, NOT an overlay: it stays readable while you play, so it never takes the
   backdrop and never closes on Escape. `pointer-events` is on the panel itself rather than on a
   parent, so the empty screen around it still passes world clicks through. */
.rpg-chat { position: absolute; left: 14px; bottom: 14px; width: 330px; z-index: 20;
  pointer-events: auto; display: flex; flex-direction: column;
  border: 1px solid var(--glass-stroke); border-radius: var(--r-3);
  background: linear-gradient(180deg, rgba(18,14,46,.93), rgba(9,7,24,.95));
  box-shadow: 0 8px 26px -10px rgba(0,0,0,.7); font-family: var(--font-body); }

.rpg-chat-head { position: relative; display: flex; align-items: center; gap: 4px;
  padding: 4px 6px; border-bottom: 1px solid var(--glass-stroke); }
.rpg-chat-tabs { display: flex; gap: 3px; flex: 1 1 auto; }
.rpg-chat-tab { font: 600 10px/1 var(--font-body); letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-soft); background: transparent; border: 1px solid transparent;
  border-radius: var(--r-pill); padding: 4px 8px; cursor: pointer; }
.rpg-chat-tab:hover { color: var(--ink); }
.rpg-chat-tab.is-active { color: var(--gold-hi); border-color: var(--glass-stroke-hi);
  background: rgba(230,196,109,.10); }

/* DM tabs. The bar WRAPS rather than clipping or scrolling: a hidden conversation with an unread
   dot you cannot see is worse than a head that grows a row, and ChatPanel caps the count anyway
   (MAX_DM_TABS) so it cannot grow without bound. */
.rpg-chat-tabs { flex-wrap: wrap; row-gap: 3px; }
.rpg-chat-tab { display: inline-flex; align-items: center; gap: 5px; }
.rpg-chat-tab-name { max-width: 74px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* THE BLINK — a few flashes on arrival, driven by the class from ChatPanel rather than a CSS
   animation, so that selecting the tab can stop it dead mid-cycle. The class is DOUBLED for
   specificity: a blinking tab is always also unread, and .has-unread would otherwise win on
   source order and swallow the gold. */
.rpg-chat-tab.is-blink.is-blink { color: var(--gold-hi); border-color: var(--gold-hi);
  background: rgba(230,196,109,.30); }
/* THE UNREAD DOT — persistent until the tab is read, and a different colour from the blink so the
   two are never mistaken for one indicator. */
.rpg-chat-tab.has-unread { color: var(--ink); }
.rpg-chat-tab-unread { color: var(--rose); font-size: 7px; line-height: 1; }
/* --ink-mute, NOT --ink-dim: --ink-dim is not defined anywhere in the design system, and a muted
   control styled with it renders as an empty gap. */
.rpg-chat-tab-close { color: var(--ink-mute); font-size: 11px; line-height: 1; opacity: .85;
  display: inline-flex; align-items: center; justify-content: center; min-width: 16px; min-height: 16px; }
.rpg-chat-tab-close:hover { color: var(--rose); opacity: 1; }

.rpg-chat-cog { flex: 0 0 auto; background: transparent; border: none; cursor: pointer;
  color: var(--ink-soft); font-size: 13px; line-height: 1; padding: 3px 4px; border-radius: 4px; }
.rpg-chat-cog:hover { color: var(--gold-hi); background: rgba(230,196,109,.10); }

.rpg-chat-menu { display: none; position: absolute; right: 4px; top: 100%; z-index: 2; margin-top: 4px;
  min-width: 168px; padding: 6px; border: 1px solid var(--glass-stroke); border-radius: var(--r-3);
  background: linear-gradient(180deg, var(--space-3), var(--space-2));
  box-shadow: 0 10px 24px -8px rgba(0,0,0,.75); }
.rpg-chat-menu.is-open { display: block; }
.rpg-chat-menu-row { display: flex; align-items: center; gap: 7px; padding: 4px 5px; cursor: pointer;
  font-size: 11px; color: var(--ink); border-radius: 4px; }
.rpg-chat-menu-row:hover { background: rgba(255,255,255,.05); }
/* Each switch is tinted like the lines it controls, so the menu is readable at a glance. */
.rpg-chat-menu-row.is-xp { color: var(--xp); }
.rpg-chat-menu-row.is-loot { color: #f2c46a; }
.rpg-chat-menu-row.is-stamps { color: #7d8496; }

/* Fixed height so the window never grows and starts covering the hotbar. */
.rpg-chat-log { height: 132px; overflow-y: auto; overflow-x: hidden; padding: 6px 8px;
  font-size: 11.5px; line-height: 1.42; color: var(--ink); }
.rpg-chat-log::-webkit-scrollbar { width: 6px; }
.rpg-chat-log::-webkit-scrollbar-thumb { background: var(--glass-stroke); border-radius: 3px; }
/* EMPTY CHANNEL. The log is a fixed-height box, so with nothing in it the bottom-left corner of the
   screen is a black rectangle for as long as the shard is quiet. The hint is supplied per tab by
   ChatPanel.render via `data-hint`, so Local, Global and Notices each explain themselves. */
.rpg-chat-log.is-empty::before {
  content: attr(data-hint);
  display: block; padding: 10px 4px 0; max-width: 30ch;
  font-family: var(--font-quill, Georgia, serif); font-style: italic; font-size: 12px;
  line-height: 1.5; color: var(--ink-mute); }
.rpg-chat-line { margin: 0 0 2px; word-wrap: break-word; overflow-wrap: anywhere; }
/* SPEAKER colours must not collide with the notice colours below, or a name reads as an XP line.
   Blue for local, pink for global; violet and gold are spoken for. */
.rpg-chat-line .who { color: #8fc9ff; font-weight: 600; }
.rpg-chat-line .who.is-global { color: #ff9ecb; }
/* Timestamps are deliberately the DIMMEST thing in the log: they are a reference you occasionally
   look for, never something to read past. Grey, and grey in every line type. */
.rpg-chat-line .stamp { color: #7d8496; font-family: var(--font-mono); font-weight: 500; font-variant-numeric: tabular-nums; font-size: 11px; }

/* Notices read as the game talking, not as a player talking — and the two KINDS are coloured
   apart, because XP and loot interleave constantly and one shared green made the log a wall of
   identical text. The same two colours label their switches in the cog menu.
   The kind is NAMESPACED (`is-kind-*`), so each kind must name its own colour here: the unprefixed
   class collided with the generic `is-system` marker and needed a :not() chain that would have
   silently coloured a future fourth kind like a refusal. */
.rpg-chat-line.is-kind-xp   { color: var(--xp); }   /* violet — matches the XP bar */
.rpg-chat-line.is-kind-loot { color: #f2c46a; }   /* gold — matches loot/gear */
/* Warm amber, deliberately NOT a reward colour: these lines are refusals ("Nowhere to land."), and
   a refusal drawn like a payout is skimmed past. */
.rpg-chat-line.is-kind-system { color: #ffb08a; }

.rpg-chat-entry { border-top: 1px solid var(--glass-stroke); padding: 5px 6px; }
.rpg-chat-input { width: 100%; box-sizing: border-box; background: rgba(7,9,26,.6);
  border: 1px solid var(--glass-stroke); border-radius: var(--r-pill); padding: 5px 9px;
  color: var(--ink); font: 400 11.5px/1.3 var(--font-body); outline: none; }
.rpg-chat-input:focus { border-color: var(--glass-stroke-hi); }
.rpg-chat-input::placeholder { color: var(--ink-dim); }

/* ── CURSOR ──────────────────────────────────────────────────────────────────────────────────
   A custom pointer, because the OS arrow is the one piece of chrome that never matched the game.
   Gold with an ink outline, so it stays readable over both the pale plaza and the dark forest
   floor — a single-colour cursor disappears against one or the other.

   The `, auto` fallback is REQUIRED, not decoration: a url() cursor with no keyword fallback is
   invalid and the whole declaration is dropped, silently. Hotspot is the arrow's own tip (0 0). */
.rpg-ui, canvas, body { cursor: url('../../cursors/arrow.png') 0 0, auto; }
/* Anything clickable keeps a normal pointer affordance rather than the world arrow. */
.rpg-ui button, .rpg-btn, .rpg-node, .rpg-slot, .rpg-chat-tab, .rpg-chat-cog,
.rpg-chat-menu-row, .rpg-close { cursor: url('../../cursors/interact.png') 0 0, pointer; }
.rpg-chat-input { cursor: text; }

